是否有Unix实用程序在stdin之前添加时间戳?


168

我最终使用Python为此编写了一个快速的小脚本,但是我想知道是否有一个实用程序可以将文本输入到每行的前面,在我的特定情况下是时间戳,您可以在其中输入文本。理想情况下,用法将类似于:

cat somefile.txt | prepend-timestamp

(在您回答sed之前,我尝试过此操作:

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

但这只会在执行sed时对date命令进行一次评估,因此同一时间戳会错误地添加到每一行。)


cat somefile.txt有点“误导”?我希望它可以“一次”发生,并且只有一个时间戳。这不是更好的测试程序(echo a; sleep 1; echo b; sleep 3; echo c; sleep 2)吗?
西罗Santilli郝海东冠状病六四事件法轮功

Answers:


185

可以尝试使用awk

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

您可能需要确保<command>产生行缓冲输出,即在每行之后刷新其输出流。时间戳awk添加将是该行的末尾出现在其输入管道上的时间。

如果awk显示错误,请尝试gawk


31
在我的系统上,“ awk”本身正在缓冲。(这对于日志文件可能是有问题的)。我使用以下命令解决了这个问题:awk'{print strftime(“%Y-%m-%dT%H:%M:%S”),$ 0; fflush(); }'
user47741 2010年

19
strftime()似乎是GNU awk扩展,因此,例如,在Mac OS上,请使用gawk而不是awk。
Joe Shaw 2012年

它并没有使我惊讶的是bash有多么强大。我认为对于这种复杂的任务不会有这么简单的解决方案。
2012年

2
对于OS X用户,您必须安装gawk并使用它而不是awk。如果您有MacPorts:sudo port install gawk
Matt Williamson

5
@teh_senaus据我所知,awkstrftime()精度不是毫秒。但是,您可以使用date命令。基本上,您只需将纳秒修剪为三个字符。它看起来是这样的:COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; done。请注意,您可以将%H:%M:%S缩写为%T。如果您仍然awk出于某些原因想要使用,可以执行以下操作:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
六点

190

tsfrom moreutils中的时间戳将在您输入的每一行之前加上时间戳。您也可以使用strftime对其进行格式化。

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

要安装它:

sudo apt-get install moreutils

2
捕获并同时加盖stderr:bad command 2>&1 | ts结果Jan 29 19:58:31 -bash: bad: command not found
rymo

我想使用时间格式2014 Mar 21 18:07:28。我如何得到它?
becko 2014年

@becko -根据手册页面,得到strftime格式字符串作为参数:[.. command ..] | ts '%Y %b %d %T',例如。
Toby Speight

2
对于OS X ,brew install moreutils。要输出UTC,可以在本地设置TZ,例如ls -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
karmakaze

1
@Dorian这是因为ruby正在缓冲输出;:如果你睡觉它工作得很好之前刷新缓冲区ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
umläute

45

注解,可以通过该链接获得,也可以annotate-output在Debian devscripts软件包中获得。

$ 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
这是一个很好的解决方案,但不适用于管道输出或带壳输出。它只会重新格式化您作为参数提供的程序或脚本的输出。
slm 2014年

1
注释输出效果很好,易于使用,但是速度很慢。
保罗·布兰南

31

将给出的答案简化为最简单的答案:

unbuffer $COMMAND | ts

在Ubuntu上,它们来自Expect-dev和moreutils软件包。

sudo apt-get install expect-dev moreutils

1
expect,不是expect-dev,但是后者引入了前者,因此可行。(Ubuntu 14.10,我想知道二进制文件是否在某个时候被转移到了另一个软件包。)
Marius Gedminas 2015年

对于Ubuntu 14.04 LTS,它仍然处于expect-dev
Willem

2
如果您希望以毫秒为单位获得经过的时间,请使用unbuffer $COMMAND | ts -s %.s-s如果经过的时间和%.s以秒为单位的时间以分数为单位)
Greg Witczak

24

这个怎么样?

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

从您希望获得实时时间戳的角度来看,也许您想对日志文件或其他内容进行实时更新?也许

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

4
这个perl示例对我来说特别有用,因为我正在以用户身份在ubuntu部署中工作,该部署显然使用了'mawk'而不是'gawk',并且没有strftime功能。谢谢!
opello

4
+1可以方便地在默认的Ubuntu安装中使用。我曾经tail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'获得过ISO8601 / RFC3339样式的日期,而不是简单版本产生的wacko。
natevw

1
如果您希望能够看到带有时间戳的日志输出,可能需要BEGIN { $|++; }在print语句之前添加以使Perl每行刷新其输出可能会有所帮助。

2
您可以将perl脚本进一步缩短到ls | perl -pne 'print localtime." "'。标量转换是由于字符串串联而发生的。
rahul 2015年

为什么同时使用-p-n选项?-n如果指定,似乎是多余的-p
Davor Cubranic


18

Kieron的答案是迄今为止最好的答案。如果由于第一个程序正在缓冲它而遇到问题,则可以使用unbuffer程序:

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

大多数Linux系统默认安装了它。如果您需要自己构建它,则它是Expect包的一部分

http://expect.nist.gov


请注意,我发现'unbuffer'有时会隐藏被调用程序的返回结果-因此在詹金斯或其他依赖返回状态的事情中没有用
oskarpearson 2014年

10

使用read(1)命令一次从标准输入中读取一行,然后使用date(1)以您选择的格式输出以日期开头的行。

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

2
有点重量级,因为它在每行中派生了一个新进程,但是它可以工作!
乔·肖

3

我不是Unix专家,但我认为您可以使用

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

这是我的awk解决方案(来自Windows / XP系统,在C:\ bin目录中安装了MKS工具)。它的设计目的是将当前日期和时间以mm / dd hh:mm的形式添加到在读取每一行时已从系统中获取了该时间戳记的每一行的开头。当然,您可以使用BEGIN模式获取时间戳一次,并将该时间戳添加到每条记录中(全部相同)。我这样做是为了将生成的日志文件标记为带有日志消息生成时的时间戳的stdout。

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

其中“模式”是要在输入行中匹配的字符串或正则表达式(不带引号),如果希望匹配所有输入行,则为可选。

这也应该在Linux / UNIX系统上工作,只需摆脱C \:\\ bin \\

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

当然,这假设命令“ date”将您带到标准Linux / UNIX date display / set命令,而没有特定的路径信息(也就是说,您的环境PATH变量已正确配置)。


在OS XI上,date命令之前需要一个空格。可悲的是,它似乎并没有重新评估每一行的命令。就像其他人建议的一样,我正在尝试将时间戳添加到tail -f中。
Mark Bennett

2

混合以上natevw和Frank Ch。艾格勒

它具有毫秒数,比date每次调用外部命令都要好,并且在大多数服务器中都可以找到perl。

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

带有刷新和循环读取的替代版本:

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
上面的两个缺陷:1. $ ms不对齐并且补零。2.日志中的所有%代码都会被篡改。因此,我没有使用打印行,而是使用: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup

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

您可以执行此操作(使用gnu / sed):

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

例:

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

当然,您可以使用计划日期的其他选项。只需替换date +%T为您需要的东西即可。


1

caerwyn的答案可以作为子例程运行,这将阻止每行新的进程:

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

echo testing 123 |timestamp

3
如何防止date每行产生?
Gyom

@Gyom并不能防止每行都产生日期。它可以防止外壳生成每一行。我要补充一点,在bash中,您可以执行以下操作:timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestamp在您获取的脚本中。
揭露2015年

调用date作为子进程仍然意味着为每一行都生成它,但是首先要启动脚本。
彼得·汉森

3
为了防止date每行生成命令,请尝试使用%(datespec)T格式的printf内置bash 。因此可能看起来像。Shell内置插件不会生成。Datespec格式类似于即格式。这需要bash,不能确定,因为它支持该说明符的版本。timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }strftime(3)dateprintf
Mindaugas Kubilius

1

免责声明:我提出的解决方案不是Unix内置实用程序。

几天前我遇到了类似的问题。我不喜欢上面解决方案的语法和局限性,所以我很快在Go中组合了一个程序来为我完成工作。

您可以在此处检查该工具:preftime

GitHub项目的“ 发行版”部分中有针对Linux,MacOS和Windows的预构建可执行文件。

该工具处理不完整的输出行,并且(从我的角度来看)具有更紧凑的语法。

<command> | preftime

这不是很理想,但是我会分享它,以防它对某人有所帮助。


0

在OSX上使用dateand tr和这样做xargs

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

如果要毫秒:

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

但请注意,在OSX上,date不会为您提供%N选项,因此您需要安装gdate(brew install coreutils),然后最终达到以下目的:

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

-1

如果您要附加的值在每一行都相同,请使用该文件启动emacs,然后:

Ctrl + <space>

在文件的开头(以标记该点),然后向下滚动到最后一行的开头(Alt +>将转到文件的结尾...这可能也需要使用Shift键,然后按Ctrl + a转到该行的开头),然后:

Ctrl + x r t

这是要在您刚刚指定的矩形(宽度为0的矩形)上插入的命令。

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

或任何您想添加的内容...然后,您将看到该文本添加到0宽度矩形内的每一行。

更新:我只是意识到您不希望使用相同的日期,因此这将无法工作...尽管您可以使用稍微复杂一些的自定义宏在emacs中执行此操作,但是仍然可以进行这种矩形编辑很高兴知道...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.