282 shell timestamps
我希望在命令的每一行输出前添加一个时间戳。例如:
foo
bar
baz
Run Code Online (Sandbox Code Playgroud)
会成为
[2011-12-13 12:20:38] foo
[2011-12-13 12:21:32] bar
[2011-12-13 12:22:20] baz
Run Code Online (Sandbox Code Playgroud)
...前缀的时间是打印行的时间。我怎样才能做到这一点?
Mar*_*try 411
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
Run Code Online (Sandbox Code Playgroud)
您想知道您重新启动的服务器何时恢复?刚刚运行ping | ts,问题解决了:D。
注意:[%Y-%m-%d %H:%M:%.S]用于微秒精度。
Chr*_*own 148
首先,如果您希望这些时间戳实际上代表一个事件,请记住,由于许多程序执行行缓冲(有些程序比其他程序更积极),重要的是要将此时间视为接近原始行的时间被打印而不是发生动作的时间戳。
您可能还想检查您的命令是否还没有专门用于执行此操作的内置功能。例如,ping -D存在于某些ping版本中,并在每行之前打印自 Unix 纪元以来的时间。但是,如果您的命令不包含自己的方法,则可以使用一些方法和工具,其中包括:
请记住,由于许多 shell 在内部将其字符串存储为 cstring,如果输入包含空字符 ( \0),则可能会导致该行过早结束。
command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done
Run Code Online (Sandbox Code Playgroud)
command | gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'
Run Code Online (Sandbox Code Playgroud)
command | perl -pe 'use POSIX strftime; print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'
Run Code Online (Sandbox Code Playgroud)
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 )))'
Run Code Online (Sandbox Code Playgroud)
command | ruby -pe 'print Time.now.strftime("[%Y-%m-%d %H:%M:%S] ")'
Run Code Online (Sandbox Code Playgroud)
4ae*_*1e1 11
我刚刚为解决这个问题而写的无耻插件:ets用 Go 编写的。
您可以在项目页面上找到很多使用示例。
\n与现有答案和类似产品的显着区别在于,它ets旨在为您在 pty(伪 tty)\xe2\x80\x94 中运行命令,也就是说,模拟在 tty 中本机运行的命令。与将命令输出通过管道传输到 eg 相比ts,这使得时间戳基本上是透明的,并解决了一系列管道问题:
命令可以直接执行,这意味着您可以简单地添加ets到现有的命令行中,或者它们也可以是 shell 命令(如上面的 gif 所示)。当然,如果你想通过管道输出,ets也可以这样做。
ets支持与 moreutils 相同的时间戳模式ts:绝对时间模式、经过时间模式和增量时间模式。它使用更合理的默认值(例如,单调时钟始终用于经过/增量时间戳),并且对自定义时区有额外的支持。这里有详细的比较。
再次,https://github.com/zmwangx/ets。试一试,报告错误等。
\n我更愿意在上面发表评论,但我不能,在声誉上。无论如何,上面的 Perl 示例可以无缓冲,如下所示:
command | perl -pe 'use POSIX strftime;
$|=1;
select((select(STDERR), $| = 1)[0]);
print strftime "[%Y-%m-%d %H:%M:%S] ", localtime'
Run Code Online (Sandbox Code Playgroud)
第一个“$|” 取消缓冲标准输出。第二个将 stderr 设置为当前默认输出通道并取消缓冲它。由于 select 返回 $| 的原始设置,通过将 select 包裹在 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)'
Run Code Online (Sandbox Code Playgroud)
Ryan 的帖子确实提供了一个有趣的想法,但是,它在几个方面都失败了。在使用 进行测试时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.
Run Code Online (Sandbox Code Playgroud)
我提出的解决方案是类似的,但是提供了适当的时间戳并使用更便携printf而不是echo
| xargs -L 1 bash -c 'printf "[%s] %s\n" "$(date +%Y-%m-%d\ %H:%M:%S )" "$*" ' bash
Run Code Online (Sandbox Code Playgroud)
为什么bash -c '...' bash?因为由于-c选项,第一个参数被分配给$0并且不会出现在输出中。请查阅您的 shell 的手册页以获得正确的描述-c
使用tail -f /var/log/syslog和(您可能会猜到)断开并重新连接到我的 wifi 来测试此解决方案,已显示date和syslog消息提供的正确时间戳
Bash 可以被任何类似 bourne 的 shell 替换,可以用ksh或来完成dash,至少可以-c选择那些。
该解决方案需要具有xargs,它在符合 POSIX 的系统上可用,因此应该涵盖大多数类 Unix 系统。如果您的系统不符合 POSIX 标准或没有GNU findutils
大多数答案都建议使用date,但它足够慢。如果您的 bash 版本大于 4.2.0,最好printf改用它,它是 bash 内建的。如果您需要支持旧版 bash 版本,您可以log根据 bash 版本创建函数:
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
Run Code Online (Sandbox Code Playgroud)
查看速度差异:
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
Run Code Online (Sandbox Code Playgroud)
UPD:$(date +"${TIMESTAMP_FORMAT}")最好使用$(exec date +"${TIMESTAMP_FORMAT}")或什至$(exec -c date +"${TIMESTAMP_FORMAT}")过于加速执行。
UPD2:bash 5 提供EPOCHREALTIME了微秒粒度的变量,你可以通过这个命令使用它(比秒慢大约 30%):
printf "%(${TIMESTAMP_FORMAT})T.%s %5d %s\n" ${EPOCHREALTIME/./ } $$ "$*"
小智 5
您可以使用date和 来执行此操作xargs:
... | xargs -L 1 echo `date +'[%Y-%m-%d %H:%M:%S]'` $1
Run Code Online (Sandbox Code Playgroud)
xargs -L 1告诉 xargs 对每 1 行输入运行后续命令,并且它会在执行此操作时传入第一行。echo `date +'[%Y-%m-%d %H:%M:%S]'` $1基本上与日期相呼应,并在其末尾输入参数