มียูทิลิตี้ Unix ในการเพิ่มการประทับเวลาไปยัง stdin หรือไม่?


168

ฉันลงเอยด้วยการเขียนสคริปต์สั้น ๆ สำหรับเรื่องนี้ใน Python แต่ฉันสงสัยว่ามียูทิลิตี้ที่คุณสามารถป้อนข้อความซึ่งจะเติมแต่ละบรรทัดด้วยข้อความบางส่วน - ในกรณีเฉพาะของฉัน เป็นการดีที่การใช้งานจะเป็นเช่น:

cat somefile.txt | prepend-timestamp

(ก่อนที่คุณจะตอบคำถามฉันพยายามทำสิ่งนี้:

cat somefile.txt | sed "s/^/`date`/"

แต่จะประเมินค่าคำสั่ง date เพียงครั้งเดียวเมื่อทำการประมวลผล sed ดังนั้นการประทับเวลาเดียวกันจะถูกเตรียมไว้อย่างไม่ถูกต้องกับแต่ละบรรทัด)


เป็นcat somefile.txtบิต "ความเข้าใจผิด"? ฉันคาดว่าจะเกิดขึ้น "ในครั้งเดียว" และมีการประทับเวลาเดียว นี่จะเป็นโปรแกรมทดสอบที่ดีกว่า(echo a; sleep 1; echo b; sleep 3; echo c; sleep 2)หรือไม่:
Ciro Santilli 法轮功冠状病六四事件法轮功

คำตอบ:


185

ลองใช้awk:

<command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }'

คุณอาจต้องตรวจสอบให้แน่ใจว่า<command>สร้างเอาต์พุตบัฟเฟอร์บรรทัดนั่นคือมันล้างข้อมูลสตรีมเอาต์พุตหลังจากแต่ละบรรทัด การประทับเวลาที่awkเพิ่มจะเป็นเวลาที่จุดสิ้นสุดของบรรทัดปรากฏบนอินพุตไพพ์

หาก awk แสดงข้อผิดพลาดให้ลองgawkแทน


31
ในระบบของฉัน 'awk' ตัวเองกำลังบัฟเฟอร์ (ซึ่งอาจเป็นปัญหาสำหรับไฟล์บันทึก) ฉันแก้ไขสิ่งนี้โดยใช้: awk '{พิมพ์ strftime ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
user47741

19
strftime () ดูเหมือนจะเป็นนามสกุล GNU awk ดังนั้นหากคุณใช้ Mac OS ให้ใช้ gawk แทน awk
โจชอว์

มันไม่หยุดที่จะทำให้ฉันประหลาดใจว่าแรงกระแทกนั้นแข็งแกร่งเพียงใด ฉันไม่คิดว่าจะมีทางออกที่ง่ายสำหรับงานที่ซับซ้อนนี้
อ่าน

2
สำหรับผู้ใช้ OS X, คุณต้องติดตั้งและใช้แทนgawk awkหากคุณมี MacPorts:sudo port install gawk
Matt Williamson

5
@teh_senaus เท่าที่ผมรู้awkก็strftime()ไม่ได้มีความแม่นยำมิลลิวินาที อย่างไรก็ตามคุณสามารถใช้dateคำสั่ง โดยทั่วไปคุณเพิ่งตัดเสี้ยววินาทีเป็นอักขระสามตัว COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; doneมันจะมีลักษณะเช่นนี้ โปรดทราบว่าคุณสามารถย่อ% H:% M:% S ได้ด้วย% T หากคุณยังต้องการใช้awkด้วยเหตุผลบางอย่างคุณสามารถทำสิ่งต่อไปนี้:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
6

190

tsจากmoreutilsจะเพิ่มการประทับเวลาให้กับอินพุตทุกบรรทัดที่คุณให้ไว้ คุณสามารถจัดรูปแบบโดยใช้ strftime ด้วย

$ echo 'foo bar baz' | ts
Mar 21 18:07:28 foo bar baz
$ echo 'blah blah blah' | ts '%F %T'
2012-03-21 18:07:30 blah blah blah
$ 

วิธีติดตั้ง:

sudo apt-get install moreutils

2
เพื่อจับภาพและประทับตรา stderr ในเวลาเดียวกัน: bad command 2>&1 | tsผลลัพธ์ในJan 29 19:58:31 -bash: bad: command not found
rymo

2014 Mar 21 18:07:28ฉันต้องการใช้รูปแบบเวลา ฉันจะรับมันได้อย่างไร
becko

@becko - ตามหน้าคู่มือให้ระบุstrftimeสตริงรูปแบบเป็นอาร์กิวเมนต์: [.. command ..] | ts '%Y %b %d %T'ตัวอย่างเช่น
Toby Speight

2
สำหรับ OS brew install moreutilsX, ในการส่งออก UTC คุณสามารถตั้งค่า TZ ในพื้นที่เช่นls -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
karmakaze

1
@ Dorian นี่เป็นเพราะทับทิมกำลังบัฟเฟอร์เอาต์พุต หากคุณล้างบัฟเฟอร์ก่อนนอนมันจะทำงานได้ดีพอ:ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
um

45

บันทึกย่อพร้อมใช้งานผ่านลิงก์นั้นหรือannotate-outputในdevscriptsแพ็คเกจDebian

$ echo -e "a\nb\nc" > lines
$ annotate-output cat lines
17:00:47 I: Started cat lines
17:00:47 O: a
17:00:47 O: b
17:00:47 O: c
17:00:47 I: Finished with exitcode 0

1
นี่เป็นโซลูชันที่ยอดเยี่ยม แต่จะไม่ทำงานกับเอาต์พุต piped หรือ Subshelled Output มันจะจัดรูปแบบผลลัพธ์สำหรับโปรแกรมหรือสคริปต์ที่คุณให้เป็นอาร์กิวเมนต์เท่านั้น
slm

1
คำอธิบายประกอบเอาต์พุตทำงานได้ดีและใช้งานง่าย แต่ช้ามาก
Paul Brannan

31

การกลั่นคำตอบที่ได้รับให้กับคำตอบที่ง่ายที่สุดที่เป็นไปได้:

unbuffer $COMMAND | ts

บน Ubuntu พวกเขามาจากแพ็คเกจที่คาดหวังและมีประสิทธิภาพมากกว่า

sudo apt-get install expect-dev moreutils

1
มันexpectไม่ใช่expect-devแต่อย่างหลังดึงเข้ามาในอดีตเพื่อให้ได้ผล (Ubuntu 14.10 ฉันสงสัยว่าไบนารีถูกย้ายไปยังแพคเกจอื่นในบางจุด)
Marius Gedminas

สำหรับ Ubuntu 14.04 LTS ยังคงอยู่ในexpect-dev
วิลเล็ม

2
หากคุณต้องการได้รับเวลาที่ผ่านไปด้วยความแม่นยำมิลลิวินาทีใช้unbuffer $COMMAND | ts -s %.s( -sถ้าเป็นเวลาที่ผ่านไปและ%.sเป็นวินาทีในส่วนเศษส่วน)
Greg Witczak

24

แล้วเรื่องนี้ล่ะ

cat somefile.txt | perl -pne 'print scalar(localtime()), " ";'

ตัดสินจากความปรารถนาของคุณที่จะได้รับการบันทึกเวลาสดคุณอาจต้องการอัปเดตสดบนไฟล์บันทึก อาจจะ

tail -f /path/to/log | perl -pne 'print scalar(localtime()), " ";' > /path/to/log-with-timestamps

4
ตัวอย่าง perl นี้มีประโยชน์อย่างยิ่งสำหรับฉันเนื่องจากฉันทำงานเป็นผู้ใช้ในการปรับใช้ Ubuntu ซึ่งเห็นได้ชัดว่าใช้ 'mawk' แทน 'gawk' - และไม่มีฟังก์ชัน strftime ขอบคุณ!
opello

4
+1 เพื่อสิ่งนี้เป็นประโยชน์ในการติดตั้ง Ubuntu เริ่มต้น ฉันเคยtail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'ได้รับรูปแบบวันที่ ISO8601 / RFC3339 แทนรุ่น wacko ที่ให้ผลลัพธ์ที่ง่ายกว่า
natevw

1
หากคุณต้องการให้สามารถดูเอาต์พุตของบันทึกเวลาที่มาถึงมันอาจเป็นประโยชน์ในการเพิ่มBEGIN { $|++; }ก่อนที่จะพิมพ์คำสั่งเพื่อให้ Perl ล้างเอาต์พุตกับแต่ละบรรทัด

2
คุณสามารถร่น Perl ls | perl -pne 'print localtime." "'สคริปต์ต่อไปเพียงแค่ การแปลงสเกลาร์เกิดขึ้นเนื่องจากการต่อสตริง
ราหุล

ทำไมคุณถึงใช้ทั้งตัวเลือก-pและ-nตัวเลือก ดูเหมือนว่าจะซ้ำซ้อนถ้าคุณได้ระบุไว้-n -p
Davor Cubranic

19

เพิ่งจะโยนสิ่งนี้ออกไป: มีคู่ของยูทิลิตี้ในdaemontools ที่เรียกว่าtai64nและtai64nlocalที่ทำขึ้นเพื่อเตรียม timestamps เพื่อบันทึกข้อความ

ตัวอย่าง:

cat file | tai64n | tai64nlocal

18

คำตอบของคีรอนเป็นคำตอบที่ดีที่สุด หากคุณมีปัญหาเนื่องจากโปรแกรมแรกกำลังบัฟเฟอร์ออกคุณสามารถใช้โปรแกรม unbuffer:

unbuffer <command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; }'

มันติดตั้งโดยค่าเริ่มต้นในระบบ linux ส่วนใหญ่ หากคุณต้องการสร้างด้วยตัวเองมันเป็นส่วนหนึ่งของแพ็คเกจที่คาดหวัง

http://expect.nist.gov


โปรดทราบว่าฉันพบว่า 'unbuffer' บางครั้งซ่อนผลลัพธ์การส่งคืนของโปรแกรมที่เรียกดังนั้นจึงไม่มีประโยชน์ในเจนกินส์หรือสิ่งอื่น ๆ ที่พึ่งพาสถานะการส่งคืน
oskarpearson

10

ใช้คำสั่ง read (1) เพื่ออ่านครั้งละหนึ่งบรรทัดจากอินพุตมาตรฐานจากนั้นเอาต์พุตบรรทัดที่เติมด้วยวันที่ในรูปแบบที่คุณเลือกโดยใช้วันที่ (1)

$ cat timestamp
#!/bin/sh
while read line
do
  echo `date` $line
done
$ cat somefile.txt | ./timestamp

2
เฮฟวี่เวทนิดหน่อยเพราะมันทำให้กระบวนการใหม่ต่อบรรทัด แต่มันใช้งานได้!
โจชอว์

3

ฉันไม่ใช่คน Unix แต่ฉันคิดว่าคุณสามารถใช้

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt

3
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'

2

นี่คือโซลูชัน awk ของฉัน (จากระบบ Windows / XP ที่ติดตั้งเครื่องมือ MKS ในไดเรกทอรี C: \ bin) มันถูกออกแบบมาเพื่อเพิ่มวันที่และเวลาปัจจุบันในรูปแบบ mm / dd hh: mm ไปยังจุดเริ่มต้นของแต่ละบรรทัดที่ดึงข้อมูลเวลานั้นจากระบบเมื่ออ่านแต่ละบรรทัด แน่นอนคุณสามารถใช้รูปแบบ BEGIN เพื่อดึงข้อมูลการประทับเวลาหนึ่งครั้งและเพิ่มการประทับเวลานั้นในแต่ละระเบียน (เหมือนกันทั้งหมด) ฉันทำสิ่งนี้เพื่อแท็กไฟล์บันทึกที่ถูกสร้างขึ้นเพื่อ stdout ด้วยการประทับเวลา ณ เวลาที่มีการสร้างข้อความบันทึก

/"pattern"/ "C\:\\\\bin\\\\date '+%m/%d %R'" | getline timestamp;
print timestamp, $0;

โดยที่ "pattern" เป็นสตริงหรือ regex (โดยไม่ใส่เครื่องหมายอัญประกาศ) เพื่อจับคู่ในบรรทัดอินพุตและเป็นทางเลือกถ้าคุณต้องการจับคู่บรรทัดอินพุตทั้งหมด

สิ่งนี้ควรทำงานบนระบบ Linux / UNIX ด้วยเพียงกำจัด C \: \\ bin \\ ออกจากบรรทัด

             "date '+%m/%d %R'" | getline timestamp;

แน่นอนว่าสมมติว่าคำสั่ง "date" นำคุณไปสู่คำสั่ง display / set วันที่ Linux / UNIX มาตรฐานโดยไม่มีข้อมูลพา ธ เฉพาะ (นั่นคือตัวแปร PATH สภาพแวดล้อมของคุณได้รับการกำหนดค่าอย่างถูกต้อง)


บน OS XI จำเป็นต้องมีช่องว่างก่อนคำสั่ง date น่าเศร้าที่ดูเหมือนว่าจะไม่ประเมินคำสั่งอีกครั้งสำหรับแต่ละบรรทัด เช่นเดียวกับคนอื่น ๆ ได้แนะนำฉันพยายามเพิ่มการประทับเวลาใน tail -f
Mark Bennett

2

ผสมคำตอบข้างต้นจากnatevwและ Frank Ch Eigler

มีมิลลิวินาทีทำงานได้ดีกว่าการเรียกdateคำสั่งภายนอกในแต่ละครั้งและสามารถพบ Perl ได้ในเซิร์ฟเวอร์ส่วนใหญ่

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday);
  use POSIX qw(strftime);
  ($s,$ms) = gettimeofday();
  print strftime "%Y-%m-%dT%H:%M:%S+$ms ", gmtime($s);
  '

รุ่นทางเลือกพร้อมฟลัชและอ่านในลูป:

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday); use POSIX qw(strftime);
  $|=1;
  while(<>) {
    ($s,$ms) = gettimeofday();
    print strftime "%Y-%m-%dT%H:%M:%S+$ms $_", gmtime($s);
  }'

1
ข้อบกพร่องสองข้อที่มีด้านบน: 1. $ ms ไม่ได้จัดตำแหน่งและไม่มีเบาะรอง 2. รหัส% ใด ๆ ในบันทึกจะถูกจัดการ ดังนั้นแทนที่จะพิมพ์บรรทัดฉันใช้: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup

2
$ cat somefile.txt | sed "s/^/`date`/"

คุณสามารถทำได้ (ด้วยgnu / sed ):

$ some-command | sed "x;s/.*/date +%T/e;G;s/\n/ /g"

ตัวอย่าง:

$ { echo 'line1'; sleep 2; echo 'line2'; } | sed "x;s/.*/date +%T/e;G;s/\n/ /g"
20:24:22 line1
20:24:24 line2

แน่นอนคุณสามารถใช้ตัวเลือกอื่น ๆ ของโปรแกรมวัน เพียงแทนที่date +%Tด้วยสิ่งที่คุณต้องการ


1

คำตอบของ caerwyn สามารถเรียกใช้เป็นรูทีนย่อยซึ่งจะป้องกันกระบวนการใหม่ต่อบรรทัด:

timestamp(){
   while read line
      do
         echo `date` $line
      done
}

echo testing 123 |timestamp

3
วิธีการที่ป้องกันไม่ให้dateเกิดในทุกบรรทัด?
Gyom

@Gyom มันไม่ได้ป้องกันวันที่ที่จะวางไข่ทุกบรรทัด มันช่วยป้องกันเชลล์ไม่ให้วางไข่ทุกบรรทัด ฉันจะเพิ่มที่ทุบตีคุณ coud ทำสิ่งที่ชอบ: timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestampในสคริปต์ที่คุณมา
smbear

วันที่เรียกในฐานะที่เป็น subprocess ยังหมายถึงการวางไข่สำหรับทุกบรรทัดอย่างไรก็ตามคุณเริ่มต้นสคริปต์ในครั้งแรก
ปีเตอร์แฮนเซน

3
เพื่อป้องกันคำสั่งวางไข่dateสำหรับทุกบรรทัดลองใช้ bash printfbuiltin พร้อมกับรูปแบบ% (datepec) T timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }ดังนั้นจึงอาจมีลักษณะเหมือน เชลล์ในตัวจะไม่วางไข่ รูปแบบ Datespec เป็นstrftime(3)เช่นdateรูปแบบเช่น สิ่งนี้ต้องใช้ bash แต่ไม่แน่ใจว่าเป็นรุ่นใดที่สนับสนุนตัวprintfระบุนั้น
Mindaugas Kubilius

1

ข้อจำกัดความรับผิดชอบ : วิธีแก้ปัญหาที่ฉันเสนอไม่ใช่เครื่องมือในตัว Unix

ฉันต้องเผชิญกับปัญหาที่คล้ายกันไม่กี่วันที่ผ่านมา ฉันไม่ชอบไวยากรณ์และข้อ จำกัด ของการแก้ปัญหาด้านบนดังนั้นฉันจึงรวมโปรแกรมใน Go เพื่อทำงานให้ฉันอย่างรวดเร็ว

คุณสามารถตรวจสอบเครื่องมือได้ที่นี่: ก่อนเวลา

มีไฟล์เรียกทำงานที่สร้างไว้ล่วงหน้าสำหรับ Linux, MacOS และ Windows ในส่วนReleaseของโครงการ GitHub

เครื่องมือจัดการบรรทัดเอาต์พุตที่ไม่สมบูรณ์และมี (จากมุมมองของฉัน) ไวยากรณ์ที่กระชับมากขึ้น

<command> | preftime

มันไม่เหมาะ แต่ฉันว่าฉันจะแบ่งปันในกรณีที่มันช่วยใครบางคน


0

ทำมันด้วยdateและtrและxargsใน OSX:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S\" | tr \"\n\" \" \"; echo \"{}\"'"
<command> | predate

ถ้าคุณต้องการมิลลิวินาที:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

แต่โปรดทราบว่าใน OSX วันที่ไม่ได้ให้ตัวเลือก% N ดังนั้นคุณจะต้องติดตั้ง gdate ( brew install coreutils) และในที่สุดก็มาถึงสิ่งนี้:

alias predate="xargs -I{} sh -c 'gdate +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

-1

หากค่าที่คุณกำลังเติมอยู่นั้นเหมือนกันในทุกบรรทัดให้ทำการ emacs ด้วยไฟล์จากนั้น:

Ctrl + <space>

ที่จุดเริ่มต้นของไฟล์ (เพื่อทำเครื่องหมายที่จุดนั้น) จากนั้นเลื่อนลงไปที่จุดเริ่มต้นของบรรทัดสุดท้าย (Alt +> จะไปที่จุดสิ้นสุดของไฟล์ ... ซึ่งอาจจะเกี่ยวข้องกับปุ่ม Shift ด้วยเช่นกันจากนั้น Ctrl + a ไปที่จุดเริ่มต้นของบรรทัดนั้น) และ:

Ctrl + x r t

ซึ่งเป็นคำสั่งที่จะแทรกที่สี่เหลี่ยมที่คุณเพิ่งระบุ (สี่เหลี่ยมผืนผ้าที่มีความกว้าง 0)

2008-8-21 6:45 PM <ศูนย์>

หรืออะไรก็ตามที่คุณต้องการเติม ... จากนั้นคุณจะเห็นข้อความที่เติมลงในทุกบรรทัดภายใน 0 ความกว้างสี่เหลี่ยมผืนผ้า

UPDATE: ฉันเพิ่งรู้ว่าคุณไม่ต้องการวันที่เดียวกันดังนั้นสิ่งนี้จะไม่ทำงาน ... แม้ว่าคุณจะสามารถทำสิ่งนี้ใน emacs ด้วยแมโครที่กำหนดเองที่ซับซ้อนกว่าเล็กน้อย แต่ก็ยังคงการแก้ไขรูปสี่เหลี่ยมผืนผ้าชนิดนี้คือ ค่อนข้างดีที่จะรู้เกี่ยวกับ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.