Answers:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
怎么运行的:
cat
读取称为的文件,input.log
并将其打印到其标准输出流。
通常,标准输出连接到终端,但是这个小脚本包含了一些内容,|
因此shell会将的标准输出重定向cat
到的标准输入sed
。
sed
读取数据(cat
生成数据),处理数据(根据-e
选件随附的脚本),然后将其打印到其标准输出。该脚本"s/^/$(date -R) /"
意味着将行的每个开头替换为date -R
命令生成的文本(replace命令的一般结构是:)s/pattern/replace/
。
然后根据>>
bash
将输出重定向sed
到名为output.log
(>
表示替换文件内容并>>
表示附加到末尾)的文件。
问题是$(date -R)
您在运行脚本时对它进行了一次评估,因此它将当前时间戳插入每行的开头。当前时间戳可能离生成消息的时间很远。为了避免这种情况,您必须在将消息写入文件时进行处理,而不要使用cron作业。
上面描述的标准流重定向称为pipe。您不仅可以|
在脚本中的命令之间进行重定向,还可以通过FIFO文件(也称为管道)来进行重定向。一个程序将写入文件,另一个程序将读取数据并在第一次发送时接收数据。
举个例子:
$ 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
怎么运行的:
mkfifo
创建一个命名管道
while true; do sed ... ; done
运行无限循环,并在每次迭代时都sed
重定向foo.log.fifo
到其标准输入。sed
阻止等待输入数据,然后处理收到的消息并将其打印到重定向到的标准输出中foo.log
。
此时,您必须打开一个新的终端窗口,因为循环占用了当前终端。
echo ... > foo.log.fifo
将消息打印到重定向到fifo文件的标准输出中,并sed
接收该消息,然后处理并写入常规文件。
重要的注意事项是fifo,就像任何其他管道的一侧没有连接到任何过程一样,都是没有意义的。如果您尝试写入管道,则当前进程将阻塞,直到有人读取管道另一侧的数据为止。如果要从管道读取,则该过程将阻塞,直到有人将数据写入管道为止。sed
上面的示例中的循环在您执行操作之前不执行任何操作(休眠)echo
。
对于您的特定情况,您只需配置您的应用程序即可将日志消息写入fifo文件。如果您无法配置它-只需删除原始日志文件并创建一个fifo文件。但是,请再次注意,如果sed
循环由于某种原因而终止,则尝试进入write
文件的程序将被阻止,直到有人read
从fifo退出为止。
好处是当程序将其写入文件时,将评估当前时间戳并将其附加到消息上。
tailf
为了使写入日志和处理过程更加独立,可以将两个常规文件与一起使用tailf
。应用程序将消息写入原始文件,其他进程读取新行(随后异步写入),并在写入第二个文件时处理数据。
让我们举个例子:
# 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
怎么运行的:
运行tailf
过程将遵循写入操作,bar.raw.log
并将它们打印到重定向到无限while read ... echo
循环的标准输出中。此循环执行两个操作:从标准输入读取数据到称为的缓冲区变量line
,然后将生成的带有以下缓冲数据的时间戳记写入bar.log
。
向写入一些消息bar.raw.log
。您必须在单独的终端窗口中执行此操作,因为第一个窗口将被tailf
写入后的工作所占用。非常简单。
优点是,如果您杀死应用程序,则不会阻止它tailf
。缺点是时间戳的准确性较低,并且日志文件重复。
tailf
,并添加了正确的使用方式。实际上,with的方式tailf
似乎更优雅,但是我离开了fifo方式,希望它对某人有用。
您可以使用ts
来自moreutils
以下方面的perl脚本:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
根据Dmitry Vasilyanov的答案进行了修改。
在bash脚本中,您可以动态重定向和包装带有时间戳的输出。
何时使用:
tailf
Dmitry Vasilyanov所说的日志文件。名为的示例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"
结果:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
怎么运行的
exec &>
将stdout和stderr重定向到同一位置>( ... )
管道输出到异步内部命令例如:
管道时间戳记并记录到文件
#!/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
或打印时间戳并登录到标准输出
#!/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
然后将它们保存在/etc/crontab
设置中
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
我使用ts
这种方法在错误日志中获得了带有时间戳的条目,该脚本用于使Cacti充满远程主机的统计信息。
为了测试仙人掌,我使用rand
了一些随机值,这些值用于温度图来监视系统温度。
Pushmonstats.sh是一个脚本,用于收集我的PC的系统温度统计信息并将其发送到运行Cacti的Raspberry Pi。前段时间,网络停滞了。我的错误日志中只有SSH超时。不幸的是,该日志中没有时间条目。我不知道如何在日志条目中添加时间戳。因此,在Internet上进行了一些搜索之后,我偶然发现了这篇文章,这就是我使用的内容ts
。
为了测试它,我使用了一个未知选项rand
。这给标准错误。为了捕获它,我将其重定向到一个临时文件。然后,我使用cat来显示文件的内容并将其通过管道传输到ts
,添加我在本文中找到的时间格式,最后将其记录到错误文件中。然后,我清除临时文件的内容,否则对于相同的错误,我将得到两次输入。
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
这在我的错误日志中提供了以下内容:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
也许这不是一个非常优雅的方法,但是它可以工作。我想知道是否有更优雅的方法。