Câu trả lời:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Làm thế nào nó hoạt động:
cat
đọc tệp được gọi input.log
và chỉ in nó ra luồng đầu ra tiêu chuẩn của nó.
Thông thường, đầu ra tiêu chuẩn được kết nối với một thiết bị đầu cuối, nhưng tập lệnh nhỏ này chứa |
shell để chuyển hướng đầu ra cat
tiêu chuẩn thành đầu vào tiêu chuẩn của sed
.
sed
đọc dữ liệu (như cat
tạo ra nó), xử lý nó (theo kịch bản được cung cấp với -e
tùy chọn) và sau đó in nó ra đầu ra tiêu chuẩn của nó. Kịch bản "s/^/$(date -R) /"
có nghĩa là thay thế mọi bắt đầu của dòng thành một văn bản được tạo bởi date -R
lệnh (cấu trúc chung cho lệnh thay thế là s/pattern/replace/
:).
Sau đó, theo >>
bash
chuyển hướng đầu ra của sed
một tệp được gọi output.log
( >
có nghĩa là thay thế nội dung tệp và >>
có nghĩa là nối vào cuối).
Vấn đề là được $(date -R)
đánh giá một lần khi bạn chạy tập lệnh để nó sẽ chèn dấu thời gian hiện tại vào đầu mỗi dòng. Dấu thời gian hiện tại có thể cách xa thời điểm một tin nhắn được tạo. Để tránh điều đó, bạn phải xử lý các tin nhắn khi chúng được ghi vào tệp, không phải với một công việc định kỳ.
Chuyển hướng dòng tiêu chuẩn được mô tả ở trên được gọi là đường ống . Bạn có thể chuyển hướng nó không chỉ với |
giữa các lệnh trong tập lệnh, mà thông qua một tệp FIFO (còn được gọi là ống ). Một chương trình sẽ ghi vào tệp và chương trình khác sẽ đọc dữ liệu và nhận nó khi gửi lần đầu tiên.
Chọn một ví dụ:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
Làm thế nào nó hoạt động:
mkfifo
tạo ra một đường ống có tên
while true; do sed ... ; done
chạy một vòng lặp vô hạn và tại mỗi lần lặp, nó chạy sed
với chuyển hướng foo.log.fifo
đến đầu vào tiêu chuẩn của nó; sed
chặn chờ dữ liệu đầu vào và sau đó xử lý tin nhắn nhận được và in nó sang đầu ra tiêu chuẩn được chuyển hướng đến foo.log
.
Tại thời điểm này, bạn phải mở một cửa sổ thiết bị đầu cuối mới vì vòng lặp chiếm thiết bị đầu cuối hiện tại.
echo ... > foo.log.fifo
in một thông điệp đến đầu ra tiêu chuẩn của nó được chuyển hướng đến tệp fifo và sed
nhận nó và xử lý và ghi vào một tệp thông thường.
Lưu ý quan trọng là fifo giống như bất kỳ đường ống nào khác không có ý nghĩa nếu một trong các mặt của nó không được kết nối với bất kỳ quá trình. Nếu bạn cố gắng ghi vào một đường ống, quy trình hiện tại sẽ chặn cho đến khi ai đó đọc dữ liệu ở phía bên kia của đường ống. Nếu bạn muốn đọc từ một đường ống, quy trình sẽ chặn cho đến khi ai đó sẽ ghi dữ liệu vào đường ống. Các sed
vòng lặp trong ví dụ trên không có gì (ngủ) cho đến khi bạn làm echo
.
Đối với tình huống cụ thể của bạn, bạn chỉ cần cấu hình ứng dụng của mình để viết thông điệp tường trình vào tệp fifo. Nếu bạn không thể định cấu hình nó - chỉ cần xóa tệp nhật ký gốc và tạo tệp fifo. Nhưng lưu ý một lần nữa rằng nếu sed
vòng lặp sẽ chết vì một số lý do - chương trình của bạn sẽ bị chặn khi cố gắng truy write
cập tệp cho đến khi có ai đó read
từ fifo.
Lợi ích là dấu thời gian hiện tại được đánh giá và đính kèm vào một thông báo khi chương trình ghi nó vào tệp.
tailf
Để ghi vào nhật ký và xử lý độc lập hơn, bạn có thể sử dụng hai tệp thông thường với tailf
. Một ứng dụng sẽ viết tin nhắn vào một tệp thô và quá trình khác đọc các dòng mới (làm theo để ghi không đồng bộ) và xử lý dữ liệu bằng cách ghi vào tệp thứ hai.
Hãy lấy một ví dụ:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
Làm thế nào nó hoạt động:
Chạy tailf
tiến trình sẽ theo ghi bar.raw.log
và in chúng thành đầu ra tiêu chuẩn được chuyển hướng đến while read ... echo
vòng lặp vô hạn . Vòng lặp này thực hiện hai hành động: đọc dữ liệu từ đầu vào tiêu chuẩn đến một biến đệm được gọi line
và sau đó ghi dấu thời gian được tạo với dữ liệu được đệm sau đây vào bar.log
.
Viết một số tin nhắn cho bar.raw.log
. Bạn phải làm điều này trong một cửa sổ đầu cuối riêng biệt bởi vì cái đầu tiên sẽ bị chiếm giữ bởi tailf
nó sẽ theo sau ghi và thực hiện công việc của nó. Khá đơn giản.
Ưu điểm là ứng dụng của bạn sẽ không chặn nếu bạn giết tailf
. Nhược điểm là dấu thời gian ít chính xác hơn và sao chép các tệp nhật ký.
tailf
, thêm cách sử dụng đúng. Trên thực tế, con đường với tailf
dường như thanh lịch hơn, nhưng tôi đã nhảy theo con đường fifo với hy vọng nó sẽ hữu ích cho ai đó.
Bạn có thể sử dụng ts
tập lệnh perl từ moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Được sửa đổi từ câu trả lời của Dmitry Vasilyanov.
Trong bash script, bạn có thể chuyển hướng và kết thúc đầu ra với dấu thời gian theo từng dòng một.
Khi nào nên sử dụng:
tailf
tệp nhật ký như Dmitry Vasilyanov nói.Một ví dụ có tên foo.sh
:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
Và kết quả:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Làm thế nào nó hoạt động
exec &>
Chuyển hướng stdout và stderr đến cùng một nơi>( ... )
đầu ra ống cho một lệnh bên trong không đồng bộVí dụ:
dấu thời gian ống và đăng nhập vào tập tin
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Hoặc in dấu thời gian và đăng nhập vào thiết bị xuất chuẩn
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
sau đó lưu chúng trong /etc/crontab
cài đặt
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Tôi đã sử dụng ts
cách này để có được một mục có dấu thời gian trong nhật ký lỗi cho tập lệnh mà tôi sử dụng để đưa Cacti chứa đầy các số liệu thống kê của máy chủ từ xa.
Để kiểm tra Cacti tôi sử dụng rand
để thêm một số giá trị ngẫu nhiên mà tôi sử dụng cho biểu đồ nhiệt độ để theo dõi nhiệt độ hệ thống của mình.
Pushmonstats.sh là một tập lệnh thu thập số liệu thống kê nhiệt độ hệ thống của PC của tôi và gửi nó đến Raspberry Pi mà Cacti chạy. Một thời gian trước, mạng đã bị kẹt. Tôi chỉ nhận được thời gian SSH trong nhật ký lỗi của tôi. Thật không may, không có mục thời gian trong nhật ký đó. Tôi không biết cách thêm dấu thời gian vào mục nhật ký. Vì vậy, sau một số tìm kiếm trên Internet, tôi tình cờ thấy bài đăng này và đây là những gì tôi đã sử dụng ts
.
Để kiểm tra nó, tôi đã sử dụng một tùy chọn không xác định rand
. Mà đã đưa ra một lỗi cho stderr. Để chụp nó, tôi chuyển hướng nó đến một tập tin tạm thời. Sau đó, tôi sử dụng cat để hiển thị nội dung của tệp và chuyển nó sang ts
, thêm định dạng thời gian mà tôi tìm thấy trên bài đăng này và cuối cùng đăng nhập vào tệp lỗi. Sau đó, tôi xóa nội dung của tệp tạm thời, nếu không tôi nhận được hai mục nhập cho cùng một lỗi.
Crontab:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
Điều này đưa ra những điều sau đây trong nhật ký lỗi của tôi:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Có thể đây không phải là một cách rất thanh lịch để làm điều đó, nhưng nó hoạt động. Tôi tự hỏi nếu có một cách tiếp cận thanh lịch hơn cho nó.