การเตรียมการประทับเวลาให้กับแต่ละบรรทัดของเอาต์พุตจากคำสั่ง


182

ฉันต้องการเพิ่มการประทับเวลาลงในแต่ละบรรทัดของเอาต์พุตจากคำสั่ง ตัวอย่างเช่น:

foo
bar
baz

จะกลายเป็น

[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz

... โดยที่เวลานำหน้าคือเวลาที่บรรทัดถูกพิมพ์ ฉันจะบรรลุสิ่งนี้ได้อย่างไร


คำตอบ:


274

moreutilsรวมถึงtsสิ่งนี้ทำได้ค่อนข้างดี:

command | ts '[%Y-%m-%d %H:%M:%S]'

มันไม่จำเป็นต้องมีการวนซ้ำเช่นกันทุกบรรทัดของเอาต์พุตจะมีการประทับเวลาวางไว้

$ echo -e "foo\nbar\nbaz" | ts '[%Y-%m-%d %H:%M:%S]'
[2011-12-13 22:07:03] foo
[2011-12-13 22:07:03] bar
[2011-12-13 22:07:03] baz

คุณต้องการที่จะรู้ว่าเมื่อเซิร์ฟเวอร์นั้นกลับมาคุณรีสตาร์ท? เพียงแค่เรียกใช้ping | tsแก้ไขปัญหา: D


8
ฉันไม่รู้เรื่องนี้ได้อย่างไร?!?!?! หางนี้เติมเต็ม -f น่าอัศจรรย์! tail -f /tmp/script.results.txt | ts
Bruno Bronosky

แล้ว cygwin ล่ะ? มีอะไรที่คล้ายกันไหม? ไม่ปรากฏว่ามีสิ่งอื่น ๆ เพิ่มเติมจาก Joey
CrazyPenguin

3
ถ้าฉันไม่มีคำสั่ง ts ฉันควรใช้อะไรดี?
ekassis

1
หากไม่ได้ผลให้ลองเปลี่ยนเส้นทาง stderr ไปที่ stdout เช่นssh -v 127.0.0.1 2>&1 | ts
jchook

3
ฉันคิดว่าการชี้พารามิเตอร์-sเป็นประโยชน์ ตามที่แสดงรันไทม์ของคำสั่ง ฉันชอบใช้ทั้งส่วนตัวtsและts -sในเวลาเดียวกัน command | ts -s '(%H:%M:%.S)]' | ts '[%Y-%m-%d %H:%M:%S'มีลักษณะบางอย่างเช่นนี้ วิธีนี้จะเป็นการเตรียมบันทึกบรรทัดดังนี้:[2018-12-04 08:31:00 (00:26:28.267126)] Hai <3
BrainStone

100

ประการแรกหากคุณคาดหวังว่าการประทับเวลาเหล่านี้จะเป็นตัวแทนของเหตุการณ์จริง ๆ โปรดจำไว้ว่าเนื่องจากโปรแกรมจำนวนมากดำเนินการบัฟเฟอร์บรรทัด ได้รับการพิมพ์มากกว่าการประทับเวลาของการกระทำที่เกิดขึ้น

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

POSIX เชลล์

โปรดจำไว้ว่าเนื่องจากเชลล์จำนวนมากเก็บสตริงไว้ภายในเป็น cstrings ถ้าอินพุตมีอักขระ null ( \0) อาจทำให้บรรทัดสิ้นสุดก่อนกำหนด

command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done

GNU awk

command | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'

Perl

command | perl -pe 'use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

หลาม

command | python -c 'import sys,time;sys.stdout.write("".join(( " ".join((time.strftime("[%Y-%m-%d %H:%M:%S]", time.localtime()), line)) for line in sys.stdin )))'

ทับทิม

command | ruby -pe 'print Time.now.strftime("[%Y-%m-%d %H:%M:%S] ")'

3
ปัญหาหนึ่งที่นี่คือโปรแกรมจำนวนมากเปิดบัฟเฟอร์เอาต์พุตมากขึ้นเมื่อ stdout ของพวกเขาเป็นไพพ์แทนเทอร์มินัล
cjm

3
@cjm - จริง การบัฟเฟอร์เอาต์พุตบางตัวสามารถบรรเทาได้ด้วยการใช้stdbuf -o 0แต่ถ้าโปรแกรมจัดการการบัฟเฟอร์ออกด้วยตนเองมันจะไม่ช่วย (เว้นแต่จะมีตัวเลือกให้ปิด / ลดขนาดของบัฟเฟอร์เอาต์พุต)
Chris Down

2
สำหรับไพ ธ อนคุณสามารถปิดการใช้งานการบัฟเฟอร์บรรทัดด้วยpython -u
ibizaman

@Bwmat ไม่... for x in sys.stdinวนซ้ำทุกบรรทัดโดยไม่บัฟเฟอร์ในหน่วยความจำก่อน
Chris Down

ทำเช่นนี้และคุณจะได้รับบัฟเฟอร์ ... สำหรับใน 1 1 1 1 1 1 นอนหลับ 1 สะท้อน; เสร็จสิ้น python -c 'import sys, เวลา; sys.stdout.write ("". เข้าร่วม (("" .join ((time.strftime ("[% Y-% m-% d% H:% M:% S]) ", time.gmtime ()), บรรทัด)) สำหรับบรรทัดใน sys.stdin))) '
ChuckCottrill

41

สำหรับบรรทัดโดยบรรทัดวัดเดลต้าพยายามแสงแดด

มันเป็นยูทิลิตี้บรรทัดคำสั่ง, บิตเช่น ts moreutils ของ, เพื่อเตรียมข้อมูลการประทับเวลาไปยังเอาต์พุตมาตรฐานของคำสั่งอื่น มีประโยชน์สำหรับกระบวนการที่ใช้เวลายาวนานซึ่งคุณต้องการให้บันทึกประวัติของสิ่งที่ใช้เวลานาน

การส่งทุกอย่างไปที่ gnomon จะเป็นการเพิ่มเวลาประทับให้กับแต่ละบรรทัดเพื่อระบุว่าบรรทัดนั้นเป็นบรรทัดสุดท้ายในบัฟเฟอร์ - นั่นคือระยะเวลาที่บรรทัดถัดไปปรากฏ โดยค่าเริ่มต้น gnomon จะแสดงวินาทีที่ผ่านไประหว่างแต่ละบรรทัด แต่ที่สามารถกำหนดค่าได้

การสาธิต gnomon


ดูเหมือนเป็นทางเลือกที่ยอดเยี่ยมtsเมื่อใช้กระบวนการแบบสด ในขณะtsที่เหมาะกว่าสำหรับกระบวนการที่ไม่มีการโต้ตอบ
BrainStone

7

โพสต์ของไรอันให้แนวคิดที่น่าสนใจอย่างไรก็ตามมันล้มเหลวในหลาย ๆ ด้าน ขณะทำการทดสอบtail -f /var/log/syslog | xargs -L 1 echo $(date +'[%Y-%m-%d %H:%M:%S]') $1 ฉันสังเกตว่าการประทับเวลายังคงเหมือนเดิมแม้ว่าจะstdoutมาภายหลังด้วยความแตกต่างในไม่กี่วินาที พิจารณาผลลัพธ์นี้:

[2016-07-14 01:44:25] Jul 14 01:44:32 eagle dhclient[16091]: DHCPREQUEST of 192.168.0.78 on wlan7 to 255.255.255.255 port 67 (xid=0x411b8c21)
[2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: Joining mDNS multicast group on interface wlan7.IPv6 with address fe80::d253:49ff:fe3d:53fd.
[2016-07-14 01:44:25] Jul 14 01:44:34 eagle avahi-daemon[740]: New relevant interface wlan7.IPv6 for mDNS.

โซลูชันที่นำเสนอของฉันคล้ายกัน แต่ให้เวลาที่เหมาะสมและใช้ค่อนข้างพกพาprintfมากกว่าecho

| xargs -L 1 bash  -c 'printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S )" "$*" ' bash

ทำไมbash -c '...' bash? เนื่องจากเนื่องจาก-cตัวเลือกอาร์กิวเมนต์แรกจะถูกกำหนดให้$0และจะไม่แสดงในเอาต์พุต ศึกษาหน้าคู่มือของเชลล์ของคุณสำหรับคำอธิบายที่เหมาะสม-c

การทดสอบโซลูชันนี้ด้วยtail -f /var/log/syslogและ (อย่างที่คุณอาจคาดเดาได้) การตัดการเชื่อมต่อและเชื่อมต่อกับ wifi ของฉันอีกครั้งแสดงให้เห็นว่าการกดเวลาที่ถูกต้องทั้งจากข้อความdateและsyslog

Bash สามารถถูกแทนที่ด้วยเปลือกที่คล้ายกับ bourne สามารถทำได้ด้วยอย่างใดอย่างหนึ่งkshหรือdashอย่างน้อยพวกที่มี-cตัวเลือก

ปัญหาที่อาจเกิดขึ้น:

วิธีการแก้ปัญหาต้องมีxargsซึ่งมีอยู่ในระบบที่สอดคล้องกับ POSIX ดังนั้นระบบที่มีลักษณะคล้าย Unix ส่วนใหญ่ควรได้รับการครอบคลุม เห็นได้ชัดว่าจะไม่ทำงานหากระบบของคุณไม่รองรับ POSIX หรือไม่มีGNU findutils


5

ฉันต้องการที่จะแสดงความคิดเห็นข้างต้น แต่ฉันไม่สามารถมีชื่อเสียง อย่างไรก็ตามตัวอย่าง Perl ข้างต้นสามารถ unbuffered ดังนี้

command | perl -pe 'use POSIX strftime; 
                    $|=1; 
                    select((select(STDERR), $| = 1)[0]);
                    print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'

ตัวแรก '$ |' unbuffers STDOUT อันที่สองตั้ง stderr เป็นช่องสัญญาณเอาท์พุทเริ่มต้นในปัจจุบันและ unbuffers มัน เนื่องจาก select ส่งกลับการตั้งค่าดั้งเดิมเป็น $ | ด้วยการตัดการเลือกภายในตัวเลือกเราจึงรีเซ็ต $ | เป็นค่าเริ่มต้น STDOUT

และใช่คุณสามารถตัด 'วาง' ตามที่เป็นอยู่ ฉันมีหลายเส้นเพื่อความชัดเจน

และถ้าคุณต้องการให้แม่นยำ (และคุณได้ติดตั้งTime :: Hires ):

command | perl -pe 'use POSIX strftime; use Time::HiRes gettimeofday;
                    $|=1; 
                    select((select(STDERR), $| = 1)[0]);
                    ($s,$ms)=gettimeofday();
                    $ms=substr(q(000000) . $ms,-6);
                    print strftime "[%Y-%m-%d %H:%M:%S.$ms]", localtime($s)'

1
ใช้งานได้อย่างมีเสน่ห์ไม่ต้องติดตั้งแพ็คเกจที่ไม่ได้มาตรฐาน
Jay Taylor

2

คำตอบส่วนใหญ่แนะนำให้ใช้dateแต่มันช้าพอ หากเวอร์ชันทุบตีของคุณมากกว่า 4.2.0 จะดีกว่าที่จะใช้printfแทนมันเป็นทุบตีในตัว หากคุณต้องการสนับสนุนรุ่นทุบตีแบบดั้งเดิมคุณสามารถสร้างlogฟังก์ชั่นได้ขึ้นอยู่กับรุ่นทุบตี:

TIMESTAMP_FORMAT='%Y-%m-%dT%H:%M:%S'
# Bash version in numbers like 4003046, where 4 is major version, 003 is minor, 046 is subminor.
printf -v BV '%d%03d%03d' ${BASH_VERSINFO[0]} ${BASH_VERSINFO[1]} ${BASH_VERSINFO[2]}
if ((BV > 4002000)); then
log() {
    ## Fast (builtin) but sec is min sample for most implementations
    printf "%(${TIMESTAMP_FORMAT})T %5d %s\n" '-1' $$ "$*"  # %b convert escapes, %s print as is
}
else
log() {
    ## Slow (subshell, date) but support nanoseconds and legacy bash versions
    echo "$(date +"${TIMESTAMP_FORMAT}") $$ $*"
}
fi

ดูความแตกต่างความเร็ว:

user@host:~$time for i in {1..10000}; do printf "%(${TIMESTAMP_FORMAT})T %s\n" '-1' "Some text" >/dev/null; done

real    0m0.410s
user    0m0.272s
sys     0m0.096s
user@host:~$time for i in {1..10000}; do echo "$(date +"${TIMESTAMP_FORMAT}") Some text" >/dev/null; done

real    0m27.377s
user    0m1.404s
sys     0m5.432s

UPD: แทนที่จะ$(date +"${TIMESTAMP_FORMAT}")เป็นการดีกว่าที่จะใช้งาน$(exec date +"${TIMESTAMP_FORMAT}")หรือแม้กระทั่ง$(exec -c date +"${TIMESTAMP_FORMAT}")การประมวลผลที่เร่งความเร็วเกินไป


0

คุณสามารถทำได้ด้วยdateและxargs:

... | xargs -L 1 echo `date +'[%Y-%m-%d %H:%M:%S]'` $1

คำอธิบาย:

xargs -L 1บอกให้ xargs รันคำสั่งที่กำลังดำเนินการสำหรับอินพุต 1 บรรทัดทุกบรรทัดและจะส่งผ่านในบรรทัดแรกตามที่ทำ echo `date +'[%Y-%m-%d %H:%M:%S]'` $1โดยพื้นฐานแล้วสะท้อนวันที่ด้วยอาร์กิวเมนต์อินพุตที่ส่วนท้ายของมัน


2
วิธีการแก้ปัญหาอยู่ใกล้ แต่ไม่ได้ประทับเวลาอย่างถูกต้องเมื่อมันมาถึงการส่งออกคั่นด้วยระยะเวลานาน นอกจากนี้คุณกำลังใช้ backticks $1และยังไม่ได้ยกมา นั่นไม่ใช่สไตล์ที่ดี อ้างถึงตัวแปรเสมอ นอกจากนี้คุณกำลังใช้echoงานซึ่งไม่สามารถพกพาได้ ไม่เป็นไร แต่อาจใช้งานไม่ได้กับบางระบบ
Sergiy Kolodyazhnyy

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