是否有 Unix 实用程序可以将时间戳添加到标准输入?

发布于 2024-07-04 10:11:56 字数 307 浏览 7 评论 0原文

我最终用 Python 编写了一个快速的小脚本,但我想知道是否有一个实用程序可以将文本输入其中,在每行前面添加一些文本 - 在我的具体情况下,是时间戳。 理想情况下,用法如下:(

cat somefile.txt | prepend-timestamp

在回答 sed 之前,我尝试了以下操作:

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

但这仅在执行 sed 时计算一次 date 命令,因此相同的时间戳错误地添加到每行前面。)

I ended up writing a quick little script for this in Python, but I was wondering if there was a utility you could feed text into which would prepend each line with some text -- in my specific case, a timestamp. Ideally, the use would be something like:

cat somefile.txt | prepend-timestamp

(Before you answer sed, I tried this:

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

But that only evaluates the date command once when sed is executed, so the same timestamp is incorrectly prepended to each line.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

最近可好 2024-07-11 10:11:56

来自 moreutilsts 将为每行输入添加时间戳你给它。 您也可以使用 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

ts from moreutils will prepend a timestamp to every line of input you give it. You can format it using strftime too.

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

To install it:

sudo apt-get install moreutils
刘备忘录 2024-07-11 10:11:56

注释,可通过该链接或作为 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

annotate, available via that link or as annotate-output in the Debian devscripts package.

$ 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
绅士风度i 2024-07-11 10:11:56

可以尝试使用 awk:

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

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

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

Could try using awk:

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

You may need to make sure that <command> produces line buffered output, i.e. it flushes its output stream after each line; the timestamp awk adds will be the time that the end of the line appeared on its input pipe.

If awk shows errors, then try gawk instead.

若无相欠,怎会相见 2024-07-11 10:11:56

无需在 strftime() 中指定所有参数,除非您确实想要自定义输出格式:

   echo "abc 123 xyz\njan 765 feb" \
    \
    | gawk -Sbe 'BEGIN {_=strftime()" "} sub("^",_)'

    Sat Apr  9 13:14:53 EDT 2022 abc 123 xyz
    Sat Apr  9 13:14:53 EDT 2022 jan 765 feb

如果您有 mawk 1.3.4,则效果相同。 即使在 上>awk-没有时间特征的变体,快速 getline 可以模拟它:

echo "abc 123 xyz\njan 765 feb" \
\
| mawk2 'BEGIN {     (__="date")|getline _;
                close(__)
                        _=_" " } sub("^",_)'
   
Sat Apr  9 13:19:38 EDT 2022 abc 123 xyz
Sat Apr  9 13:19:38 EDT 2022 jan 765 feb

如果你想跳过所有 getline 和 BEGIN { },那么像这样:

mawk2 'sub("^",_" ")' \_="$(date)"

No need to specify all the parameters in strftime() unless you really want to customize the outputting format :

   echo "abc 123 xyz\njan 765 feb" \
    \
    | gawk -Sbe 'BEGIN {_=strftime()" "} sub("^",_)'

    Sat Apr  9 13:14:53 EDT 2022 abc 123 xyz
    Sat Apr  9 13:14:53 EDT 2022 jan 765 feb

works the same if you have mawk 1.3.4. Even on awk-variants without the time features, a quick getline could emulate it :

echo "abc 123 xyz\njan 765 feb" \
\
| mawk2 'BEGIN {     (__="date")|getline _;
                close(__)
                        _=_" " } sub("^",_)'
   
Sat Apr  9 13:19:38 EDT 2022 abc 123 xyz
Sat Apr  9 13:19:38 EDT 2022 jan 765 feb

If you wanna skip all that getline and BEGIN { }, then something like this :

mawk2 'sub("^",_" ")' \_="$(date)"
秋凉 2024-07-11 10:11:56

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

Ctrl +

在文件的开头(标记该位置),然后向下滚动到最后一行的开头(Alt + > 将转到文件末尾...这可能也涉及 Shift 键,然后 Ctrl + a 转到开头该行)和:

Ctrl + x r t

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

2008-8-21 6:45PM <进入>

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

更新:我刚刚意识到你不想要相同的日期,所以这不起作用......虽然你可以在 emacs 中使用稍微复杂的自定义宏来完成此操作,但是,这种矩形编辑仍然是很高兴知道...

If the value you are prepending is the same on every line, fire up emacs with the file, then:

Ctrl + <space>

at the beginning of the of the file (to mark that spot), then scroll down to the beginning of the last line (Alt + > will go to the end of file... which probably will involve the Shift key too, then Ctrl + a to go to the beginning of that line) and:

Ctrl + x r t

Which is the command to insert at the rectangle you just specified (a rectangle of 0 width).

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

Or whatever you want to prepend... then you will see that text prepended to every line within the 0 width rectangle.

UPDATE: I just realized you don't want the SAME date, so this won't work... though you may be able to do this in emacs with a slightly more complicated custom macro, but still, this kind of rectangle editing is pretty nice to know about...

怪异←思 2024-07-11 10:11:56

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

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

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

http://expect.nist.gov

Kieron's answer is the best one so far. If you have problems because the first program is buffering its out you can use the unbuffer program:

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

It's installed by default on most linux systems. If you need to build it yourself it is part of the expect package

http://expect.nist.gov

呢古 2024-07-11 10:11:56

将给出的答案提炼为最简单的答案:

unbuffer $COMMAND | ts

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

sudo apt-get install expect-dev moreutils

Distilling the given answers to the simplest one possible:

unbuffer $COMMAND | ts

On Ubuntu, they come from the expect-dev and moreutils packages.

sudo apt-get install expect-dev moreutils
风尘浪孓 2024-07-11 10:11:56

这个怎么样?

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

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

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

How about this?

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

Judging from your desire to get live timestamps, maybe you want to do live updating on a log file or something? Maybe

tail -f /path/to/log | perl -pne 'print scalar(localtime()), " ";' > /path/to/log-with-timestamps
芯好空 2024-07-11 10:11:56

只是想把这个扔出去:daemontools中有一对名为tai64ntai64nlocal 用于前置记录消息的时间戳。

例子:

cat file | tai64n | tai64nlocal

Just gonna throw this out there: there are a pair of utilities in daemontools called tai64n and tai64nlocal that are made for prepending timestamps to log messages.

Example:

cat file | tai64n | tai64nlocal
聆听风音 2024-07-11 10:11:56

这是我的 awk 解决方案(来自 Windows/XP 系统,MKS Tools 安装在 C:\bin 目录中)。 它旨在以 mm/dd hh:mm 的形式将当前日期和时间添加到每行的开头,并在读取每行时从系统获取时间戳。 当然,您可以使用 BEGIN 模式来获取时间戳一次并将该时间戳添加到每个记录中(都一样)。 我这样做是为了使用生成日志消息时的时间戳来标记正在生成到标准输出的日志文件。

/"pattern"/ "C\:\\\\bin\\\\date '+%m/%d %R'" | 获取行时间戳;
print timestamp, $0;

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

这也应该适用于 Linux/UNIX 系统,只需去掉 C\:\\bin\\ 留下该行

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

当然,假设命令“date”让您进入标准的 Linux/UNIX 日期显示/ set命令没有具体的路径信息(即你的环境PATH变量配置正确)。

Here's my awk solution (from a Windows/XP system with MKS Tools installed in the C:\bin directory). It is designed to add the current date and time in the form mm/dd hh:mm to the beginning of each line having fetched that timestamp from the system as each line is read. You could, of course, use the BEGIN pattern to fetch the timestamp once and add that timestamp to each record (all the same). I did this to tag a log file that was being generated to stdout with the timestamp at the time the log message was generated.

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

print timestamp, $0;

where "pattern" is a string or regex (without the quotes) to be matched in the input line, and is optional if you wish to match all input lines.

This should work on Linux/UNIX systems as well, just get rid of the C\:\\bin\\ leaving the line

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

This, of course, assumes that the command "date" gets you to the standard Linux/UNIX date display/set command without specific path information (that is, your environment PATH variable is correctly configured).

软的没边 2024-07-11 10:11:56
$ cat somefile.txt |   sed "s/^/`日期`/" 
  

你可以这样做(使用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 替换为您需要的内容即可。

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

you can do this (with gnu/sed):

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

example:

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

of course, you can use other options of the program date. just replace date +%T with what you need.

生生漫 2024-07-11 10:11:56
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'
╭ゆ眷念 2024-07-11 10:11:56

我不是 Unix 爱好者,但我认为你可以使用

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt

I'm not an Unix guy, but I think you can use

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt
淡忘如思 2024-07-11 10:11:56

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

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

Use the read(1) command to read one line at a time from standard input, then output the line prepended with the date in the format of your choosing using date(1).

$ cat timestamp
#!/bin/sh
while read line
do
  echo `date` $line
done
$ cat somefile.txt | ./timestamp
佼人 2024-07-11 10:11:56

其他答案大多有效,但有一些缺点。 特别是:

  1. 许多要求安装 L​​inux 系统上不常见的命令,这可能是不可能或不方便的。
  2. 由于它们使用管道,因此它们不会在 stderr 上放置时间戳,并且会丢失退出状态。
  3. 如果对 stderr 和 stdout 使用多个管道,则有些管道没有原子打印,导致输出的混合行,例如 [timestamp] [timestamp] stdout line \nstderr line
  4. 缓冲可能会导致问题,并且 < code>unbuffer 需要额外的依赖项。

为了解决(4),我们可以使用大多数Linux系统上普遍可用的stdbuf -i0 -o0 -e0(参见如何使任何 shell 命令的输出不缓冲?)。

要解决(3)问题,您只需小心地一次打印整行即可。

  • 错误:ruby -pe 'print Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \")' (打印时间戳,然后打印 $_ 的内容。)
  • 好:ruby -pe '\$_ = Time.now.strftime(\"[%Y-%m-%d %H:%M :%S] \") + \$_' (改变$_,然后打印它。)

为了解决(2),我们需要使用多个管道并保存退出状态:

alias tslines-pipe="stdbuf -i0 -o0 ruby -pe '\$_ = Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \") + \$_'"
function tslines() (
  stdbuf -o0 -e0 "$@" 2> >(tslines-pipe) > >(tslines-pipe)
  status="$?"
  exit $status
)

然后您可以使用 tslines some command --options 运行命令。

几乎有效,只是有时其中一个管道需要稍长的时间才能退出,并且tslines函数已退出,因此已打印下一个提示。 例如,此命令似乎在出现下一行提示后打印所有输出,这可能有点令人困惑:

tslines bash -c '(for (( i=1; i<=20; i++ )); do echo stderr 1>&2; echo stdout;  done)'

两个管道进程和 tslines 函数之间需要某种协调方法。 想必有很多方法可以做到这一点。 我发现的一种方法是让管道向主函数可以侦听的管道发送一些行,并且只有在从两个管道处理程序接收到数据后才退出。 综合起来:

alias tslines-pipe="stdbuf -i0 -o0 ruby -pe '\$_ = Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \") + \$_'"
function tslines() (
  # Pick a random name for the pipe to prevent collisions.
  pipe="/tmp/pipe-$RANDOM"
  
  # Ensure the pipe gets deleted when the method exits.
  trap "rm -f $pipe" EXIT

  # Create the pipe.  See https://www.linuxjournal.com/content/using-named-pipes-fifos-bash
  mkfifo "$pipe"
  # echo will block until the pipe is read.
  stdbuf -o0 -e0 "$@" 2> >(tslines-pipe; echo "done" >> $pipe) > >(tslines-pipe; echo "done" >> $pipe)
  status="$?"

  # Wait until we've received data from both pipe commands before exiting.
  linecount=0
  while [[ $linecount -lt 2 ]]; do
    read line
    if [[ "$line" == "done" ]]; then
      ((linecount++))
    fi
  done < "$pipe"
  exit $status
)

同步机制感觉有点复杂; 希望有一种更简单的方法来做到这一点。

The other answers mostly work, but have some drawbacks. In particular:

  1. Many require installing a command not commonly found on linux systems, which may not be possible or convenient.
  2. Since they use pipes, they don't put timestamps on stderr, and lose the exit status.
  3. If you use multiple pipes for stderr and stdout, then some do not have atomic printing, leading to intermingled lines of output like [timestamp] [timestamp] stdout line \nstderr line
  4. Buffering can cause problems, and unbuffer requires an extra dependency.

To solve (4), we can use stdbuf -i0 -o0 -e0 which is generally available on most linux systems (see How to make output of any shell command unbuffered?).

To solve (3), you just need to be careful to print the entire line at a time.

  • Bad: ruby -pe 'print Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \")' (Prints the timestamp, then prints the contents of $_.)
  • Good: ruby -pe '\$_ = Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \") + \$_' (Alters $_, then prints it.)

To solve (2), we need to use multiple pipes and save the exit status:

alias tslines-pipe="stdbuf -i0 -o0 ruby -pe '\$_ = Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \") + \$_'"
function tslines() (
  stdbuf -o0 -e0 "$@" 2> >(tslines-pipe) > >(tslines-pipe)
  status="$?"
  exit $status
)

Then you can run a command with tslines some command --options.

This almost works, except sometimes one of the pipes takes slightly longer to exit and the tslines function has exited, so the next prompt has printed. For example, this command seems to print all the output after the prompt for the next line has appeared, which can be a bit confusing:

tslines bash -c '(for (( i=1; i<=20; i++ )); do echo stderr 1>&2; echo stdout;  done)'

There needs to be some coordination method between the two pipe processes and the tslines function. There are presumably many ways to do this. One way I found is to have the pipes send some lines to a pipe that the main function can listen to, and only exit after it's received data from both pipe handlers. Putting that together:

alias tslines-pipe="stdbuf -i0 -o0 ruby -pe '\$_ = Time.now.strftime(\"[%Y-%m-%d %H:%M:%S] \") + \$_'"
function tslines() (
  # Pick a random name for the pipe to prevent collisions.
  pipe="/tmp/pipe-$RANDOM"
  
  # Ensure the pipe gets deleted when the method exits.
  trap "rm -f $pipe" EXIT

  # Create the pipe.  See https://www.linuxjournal.com/content/using-named-pipes-fifos-bash
  mkfifo "$pipe"
  # echo will block until the pipe is read.
  stdbuf -o0 -e0 "$@" 2> >(tslines-pipe; echo "done" >> $pipe) > >(tslines-pipe; echo "done" >> $pipe)
  status="$?"

  # Wait until we've received data from both pipe commands before exiting.
  linecount=0
  while [[ $linecount -lt 2 ]]; do
    read line
    if [[ "$line" == "done" ]]; then
      ((linecount++))
    fi
  done < "$pipe"
  exit $status
)

That synchronization mechanism feels a bit convoluted; hopefully there's a simpler way to do it.

ぽ尐不点ル 2024-07-11 10:11:56

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

几天前我遇到了类似的问题。 我不喜欢上述解决方案的语法和限制,所以我很快用 Go 编写了一个程序来帮我完成这项工作。

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

有针对 Linux 的预构建可执行文件, MacOS 和 Windows 位于 GitHub 项目的 发布 部分。

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

<代码><命令>; | preftime

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

Disclaimer: the solution I am proposing is not a Unix built-in utility.

I faced a similar problem a few days ago. I did not like the syntax and limitations of the solutions above, so I quickly put together a program in Go to do the job for me.

You can check the tool here: preftime

There are prebuilt executables for Linux, MacOS, and Windows in the Releases section of the GitHub project.

The tool handles incomplete output lines and has (from my point of view) a more compact syntax.

<command> | preftime

It's not ideal, but I though I'd share it in case it helps someone.

迷你仙 2024-07-11 10:11:56

在 OSX 上使用 datetrxargs 执行此操作:

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 \"{}\"'"

doing it with date and tr and xargs on OSX:

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

if you want milliseconds:

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

but note that on OSX, date doesn't give you the %N option, so you'll need to install gdate (brew install coreutils) and so finally arrive at this:

alias predate="xargs -I{} sh -c 'gdate +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"
梦幻之岛 2024-07-11 10:11:56

混合上面 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);
  }'

Mixing some answers above from natevw and Frank Ch. Eigler.

It has milliseconds, performs better than calling a external date command each time and perl can be found in most of the servers.

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

Alternative version with flush and read in a loop:

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);
  }'
计㈡愣 2024-07-11 10:11:56

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

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

echo testing 123 |timestamp

caerwyn's answer can be run as a subroutine, which would prevent the new processes per line:

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

echo testing 123 |timestamp
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文