Có một tiện ích Unix để thêm dấu thời gian cho stdin không?


168

Cuối cùng tôi đã viết một đoạn script nhỏ cho Python này, nhưng tôi tự hỏi liệu có một tiện ích nào bạn có thể cung cấp văn bản vào đó sẽ bổ sung mỗi dòng với một số văn bản - trong trường hợp cụ thể của tôi, dấu thời gian. Lý tưởng nhất, việc sử dụng sẽ là một cái gì đó như:

cat somefile.txt | prepend-timestamp

(Trước khi bạn trả lời sed, tôi đã thử điều này:

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

Nhưng điều đó chỉ đánh giá lệnh ngày một lần khi sed được thực thi, do đó dấu thời gian tương tự được đặt không chính xác cho mỗi dòng.)


cat somefile.txtmột chút "sai lệch"? Tôi hy vọng nó sẽ xảy ra "ngay lập tức" và có dấu thời gian duy nhất. Đây sẽ không phải là một chương trình thử nghiệm tốt hơn : (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2)?
Ciro Santilli 郝海东 冠状 病 事件

Câu trả lời:


185

Có thể thử sử dụng awk:

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

Bạn có thể cần đảm bảo rằng <command>tạo ra đầu ra được đệm dòng, tức là nó tuôn ra luồng đầu ra sau mỗi dòng; dấu thời gian awkthêm sẽ là thời gian mà cuối dòng xuất hiện trên đường ống đầu vào của nó.

Nếu awk hiển thị lỗi, sau đó thử gawkthay thế.


31
Trên hệ thống của tôi, 'awk' đã được đệm. (Điều này có thể có vấn đề cho các tệp nhật ký). Tôi đã sửa lỗi này bằng cách sử dụng: awk '{print strftime ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
user47741

19
strftime () dường như là một phần mở rộng GNU awk, vì vậy, nếu bạn đang dùng Mac OS, hãy sử dụng gawk thay vì awk.
Joe Shaw

Nó không ngừng làm tôi ngạc nhiên về sức mạnh của bash. Tôi không nghĩ sẽ có một giải pháp dễ dàng cho nhiệm vụ phức tạp này.
tái hiện

2
Đối với người dùng OS X, bạn phải cài đặt gawkvà sử dụng nó thay vì awk. Nếu bạn có MacPorts:sudo port install gawk
Matt Williamson

5
@teh_senaus Theo như tôi biết, awk's strftime()không có độ chính xác phần nghìn giây. Tuy nhiên bạn có thể sử dụng datelệnh. Về cơ bản, bạn chỉ cần cắt nano giây thành ba ký tự. Nó sẽ trông như thế này : COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; done. Lưu ý rằng bạn có thể viết tắt% H:% M:% S với% T. Nếu bạn vẫn muốn sử dụng awkvì một số lý do, bạn có thể thực hiện các thao tác sau:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
Sáu

190

tstừ moreutils sẽ thêm dấu thời gian cho mỗi dòng đầu vào bạn cung cấp. Bạn có thể định dạng nó bằng cách sử dụng strftime quá.

$ 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
$ 

Để cài đặt nó:

sudo apt-get install moreutils

2
để chụp và đóng dấu stderr cùng một lúc: bad command 2>&1 | tskết quả trongJan 29 19:58:31 -bash: bad: command not found
rymo

Tôi muốn sử dụng định dạng thời gian 2014 Mar 21 18:07:28. Làm sao để tôi có được nó?
vẫy gọi

@becko - theo trang hướng dẫn , hãy đưa ra một strftimechuỗi định dạng làm đối số :, [.. command ..] | ts '%Y %b %d %T'chẳng hạn.
Toby Speight

2
Đối với hệ điều hành X , brew install moreutils. Để đầu ra UTC, bạn có thể đặt TZ tại địa phương, ví dụls -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
karmakaze

1
@Dorian điều này là do ruby ​​đang đệm đầu ra; nếu bạn xả đệm trước khi ngủ, nó hoạt động đủ tốt:ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
umläute

45

chú thích , có sẵn thông qua liên kết đó hoặc như annotate-outputtrong devscriptsgói 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
Đây là một giải pháp tuyệt vời, nhưng sẽ không hoạt động với đầu ra đường ống hoặc đầu ra dưới vỏ. Nó sẽ chỉ định dạng lại đầu ra cho một chương trình hoặc tập lệnh mà bạn đưa ra làm đối số.
slm

1
annotate-output hoạt động độc đáo và dễ sử dụng, nhưng THỰC SỰ chậm.
Paul Brannan

31

Chắt lọc các câu trả lời cho câu trả lời đơn giản nhất có thể:

unbuffer $COMMAND | ts

Trên Ubuntu, chúng đến từ các gói kỳ vọng và nhiều hơn nữa.

sudo apt-get install expect-dev moreutils

1
expect, không phải expect-dev, nhưng cái sau kéo theo cái trước để cái này hoạt động. (Ubuntu 14.10, tôi tự hỏi liệu nhị phân có được chuyển sang một gói khác vào một lúc nào đó không.)
Marius Gedminas

Đối với Ubuntu 14.04 LTS, nó vẫn ở trongexpect-dev
Willem

2
Nếu bạn muốn có được thời gian trôi qua với độ chính xác đến mili giây, hãy sử dụng unbuffer $COMMAND | ts -s %.s( -snếu thời gian trôi qua và %.sthời gian tính bằng giây với phần phân đoạn)
Greg Witczak

24

Còn cái này thì sao?

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

Đánh giá từ mong muốn của bạn để có được dấu thời gian trực tiếp, có thể bạn muốn cập nhật trực tiếp trên một tệp nhật ký hoặc một cái gì đó? Có lẽ

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

4
Ví dụ perl này đặc biệt hữu ích đối với tôi khi tôi đang làm việc như một người dùng khi triển khai ubfox, rõ ràng là sử dụng 'mawk' thay vì 'gawk' - và không có chức năng strftime. Cảm ơn!
opello

4
+1 để tiện cho việc cài đặt Ubuntu mặc định này. Tôi đã từng tail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'có được một ngày theo kiểu ISO8601 / RFC3339 thay vì ngày lập dị mà phiên bản đơn giản hơn mang lại.
natevw

1
Nếu bạn muốn có thể nhìn vào đầu ra nhật ký được đánh dấu thời gian khi nó đến, có thể hữu ích để thêm BEGIN { $|++; }trước câu lệnh in để Perl tuôn ra đầu ra của nó với mỗi dòng.

2
Bạn có thể rút ngắn kịch bản perl hơn nữa ls | perl -pne 'print localtime." "'. Việc chuyển đổi vô hướng xảy ra do nối chuỗi.
rahul

Tại sao bạn sử dụng cả hai -p-ntùy chọn? Có vẻ như -nlà dư thừa nếu bạn đã chỉ định -p.
Davor Cuba

19

Chỉ cần ném nó ra khỏi đó: có một cặp tiện ích trong daemontools gọi là tai64ntai64nlocal được tạo ra để chuẩn bị dấu thời gian để ghi nhật ký tin nhắn.

Thí dụ:

cat file | tai64n | tai64nlocal

18

Câu trả lời của Kieron là câu trả lời hay nhất cho đến nay. Nếu bạn gặp vấn đề vì chương trình đầu tiên đang đệm, bạn có thể sử dụng chương trình unbuffer:

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

Nó được cài đặt theo mặc định trên hầu hết các hệ thống linux. Nếu bạn cần tự xây dựng nó, nó là một phần của gói mong đợi

http://recect.nist.gov


Lưu ý rằng đôi khi tôi thấy rằng 'unbuffer' che giấu kết quả trả về của chương trình được gọi - vì vậy nó không hữu ích trong Jenkins hoặc những thứ khác dựa trên trạng thái trả về
oskarpearson

10

Sử dụng lệnh read (1) để đọc từng dòng một từ đầu vào tiêu chuẩn, sau đó xuất dòng được thêm vào ngày theo định dạng bạn chọn bằng cách sử dụng ngày (1).

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

2
Một chút nặng nề vì nó tạo ra một quy trình mới trên mỗi dòng, nhưng nó hoạt động!
Joe Shaw

3

Tôi không phải là một người Unix, nhưng tôi nghĩ bạn có thể sử dụng

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

Đây là giải pháp awk của tôi (từ hệ thống Windows / XP với Công cụ MKS được cài đặt trong thư mục C: \ bin). Nó được thiết kế để thêm ngày và giờ hiện tại ở dạng mm / dd hh: mm vào đầu mỗi dòng đã lấy dấu thời gian đó từ hệ thống khi mỗi dòng được đọc. Tất nhiên, bạn có thể sử dụng mẫu BEGIN để tìm nạp dấu thời gian một lần và thêm dấu thời gian đó vào mỗi bản ghi (tất cả đều giống nhau). Tôi đã làm điều này để gắn thẻ một tệp nhật ký đang được tạo để xuất ra với dấu thời gian tại thời điểm thông điệp tường trình được tạo.

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

trong đó "mẫu" là một chuỗi hoặc regex (không có dấu ngoặc kép) được khớp trong dòng đầu vào và là tùy chọn nếu bạn muốn khớp với tất cả các dòng đầu vào.

Điều này cũng hoạt động trên các hệ thống Linux / UNIX, chỉ cần loại bỏ C \: \\ bin \\ rời khỏi dòng

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

Tất nhiên, điều này giả định rằng lệnh "date" đưa bạn đến lệnh hiển thị / set ngày Linux / UNIX tiêu chuẩn mà không có thông tin đường dẫn cụ thể (nghĩa là biến PATH môi trường của bạn được cấu hình đúng).


Trên OS XI cần một khoảng trắng trước lệnh date. Đáng buồn thay, nó dường như không đánh giá lại lệnh cho mỗi dòng. Giống như những người khác đã đề xuất, tôi đang cố gắng thêm dấu thời gian vào đuôi -f.
Đánh dấu Bennett

2

Trộn một số câu trả lời ở trên từ natevw và Frank Ch. Eigler.

Nó có mili giây, hoạt động tốt hơn so với việc gọi datelệnh bên ngoài mỗi lần và perl có thể được tìm thấy trong hầu hết các máy chủ.

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);
  '

Phiên bản thay thế với tuôn ra và đọc trong một vòng lặp:

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
Hai lỗ hổng ở trên: 1. $ ms không được căn chỉnh và không có đệm. 2. Bất kỳ mã% nào trong nhật ký sẽ được đọc sai. Do đó, thay vì dòng in, tôi đã sử dụng: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup

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

bạn có thể làm điều này (với gnu / sed ):

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

thí dụ:

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

Tất nhiên, bạn có thể sử dụng các tùy chọn khác của ngày chương trình . Chỉ cần thay thế date +%Tbằng những gì bạn cần.


1

Câu trả lời của caerwyn có thể được chạy dưới dạng chương trình con, điều này sẽ ngăn các tiến trình mới trên mỗi dòng:

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

echo testing 123 |timestamp

3
Làm thế nào mà ngăn chặn dateđược sinh sản ở mỗi dòng?
Gyom

@Gyom Nó không ngăn ngày được sinh ra mỗi dòng. Nó ngăn vỏ sinh sản mọi dòng. Tôi sẽ thêm rằng trong bash bạn coud làm một cái gì đó như: timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestamptrong một kịch bản mà bạn nguồn.
smbear

Gọi ngày như một quy trình con vẫn có nghĩa là sinh ra nó cho mỗi dòng, tuy nhiên bạn khởi chạy tập lệnh ở vị trí đầu tiên.
Peter Hansen

3
Để ngăn lệnh sinh sản datecho mỗi dòng, hãy thử sử dụng bash printfdựng sẵn với định dạng % (datepec) T. Vì vậy, nó có thể trông như thế nào timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }. Shell dựng sẽ không sinh sản. Định dạng Datespec giống như các định dạng strftime(3)tức là date. Điều này đòi hỏi bash, không chắc chắn vì nó hỗ trợ phiên bản đó printf.
Mindaugas Kubilius

1

Tuyên bố miễn trừ trách nhiệm : giải pháp tôi đang đề xuất không phải là tiện ích tích hợp sẵn Unix.

Tôi đã đối mặt với một vấn đề tương tự một vài ngày trước. Tôi không thích cú pháp và giới hạn của các giải pháp ở trên, vì vậy tôi nhanh chóng kết hợp một chương trình trong Go để thực hiện công việc cho tôi.

Bạn có thể kiểm tra công cụ ở đây: preftime

Có các tệp thực thi dựng sẵn cho Linux, MacOS và Windows trong phần Phát hành của dự án GitHub.

Công cụ xử lý các dòng đầu ra không đầy đủ và có (theo quan điểm của tôi) một cú pháp nhỏ gọn hơn.

<command> | preftime

Điều đó không lý tưởng, nhưng tôi mặc dù tôi muốn chia sẻ nó trong trường hợp nó giúp được ai đó.


0

làm việc đó với datetrxargstrên OSX:

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

nếu bạn muốn một phần nghìn giây:

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

nhưng lưu ý rằng trên OSX, ngày không cung cấp cho bạn tùy chọn% N, vì vậy bạn sẽ cần cài đặt gdate ( brew install coreutils) và cuối cùng sẽ đến đây:

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

-1

Nếu giá trị bạn đang chuẩn bị giống nhau trên mỗi dòng, hãy khởi động emacs với tệp, sau đó:

Ctrl + <space>

ở đầu tập tin (để đánh dấu vị trí đó), sau đó cuộn xuống đầu dòng cuối cùng (Alt +> sẽ đi đến cuối tập tin ... có lẽ cũng sẽ liên quan đến phím Shift, sau đó Ctrl + a để đi đến đầu dòng đó) và:

Ctrl + x r t

Đó là lệnh để chèn vào hình chữ nhật bạn vừa chỉ định (một hình chữ nhật có chiều rộng 0).

2008-8-21 6:45 PM <enter>

Hoặc bất cứ điều gì bạn muốn trả trước ... thì bạn sẽ thấy văn bản đó được thêm vào mỗi dòng trong hình chữ nhật có chiều rộng 0.

CẬP NHẬT: Tôi mới nhận ra rằng bạn không muốn ngày CÙNG, vì vậy điều này sẽ không hoạt động ... mặc dù bạn có thể thực hiện điều này trong các emacs với macro tùy chỉnh phức tạp hơn một chút, nhưng vẫn, loại chỉnh sửa hình chữ nhật này là khá hay để biết về ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.