shell 当重定向到日志文件时,如何在stdout和stderr输出前添加时间戳?

q35jwt9p  于 12个月前  发布在  Shell
关注(0)|答案(4)|浏览(179)

在Linux中,我在一个init脚本(SysVInit)中启动了一个名为$cmd的程序。我已经将$cmd的stdout和stderr重定向到两个不同的日志文件,分别名为$stdout_log$stderr_log。现在我还想在打印到日志文件中的每一行前面添加一个时间戳。
我尝试编写一个名为log_pipe的函数,如下所示:

log_pipe() {
    while read line; do
        echo [$(date +%Y-%m-%d\ %H:%M:%S)] "$line"
    done
}

然后将我的脚本输出到这个函数中,然后将它们重定向到日志文件,如下所示:

$cmd | log_pipe >> "$stdout_log" 2>> "$stderr_log" &

我得到的是一个空的$stdout.log(stdout),这应该没问题,因为$cmd通常不打印任何东西。和一个只有时间戳但没有错误文本的$stderr.log文件。
我的错误推理在哪里?
PS:因为问题存在于一个初始化脚本中,我只想使用基本的shell命令,没有额外的包。

e1xvtsh3

e1xvtsh31#

在任何POSIX shell中,尝试:

{ cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log

此外,如果你有GNU awk(有时称为gawk),那么log_pipe可以变得更简单,更快:

log_pipe() {  awk '{print strftime("[%Y-%m-%d %H:%M:%S]"),$0}'; }

示例

例如,让我们创建命令cmd

cmd() { echo "This is out"; echo "This is err">&2; }

现在,让我们运行命令并查看输出文件:

$ { cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log
$ cat stdout.log
[2019-07-04 23:42:20] This is out
$ cat stderr.log
[2019-07-04 23:42:20] This is err

问题

cmd | log_pipe >> "$stdout_log" 2>> "$stderr_log"

上面的命令将标准输出从cmd重定向到log_pipelog_pipe的stdout重定向到$stdout_loglog_pipe的stderr重定向到$stderr_log问题是cmd的stderr永远不会重定向。它直接通往终点站。
例如,考虑cmd

cmd() { echo "This is out"; echo "This is err">&2; }

现在,让我们运行命令:

$ cmd | log_pipe >>stdout.log 2>>stderr.log
This is err

我们可以看到This is err没有发送到文件stderr.log。相反,它出现在终端上。它从未被log_pipe看到过。stderr.log仅捕获来自log_pipe的错误消息。

ybzsozfc

ybzsozfc2#

在Bash中,你也可以使用进程替换重定向到子shell:

logger.sh

#!/bin/bash
while read -r line; do
   echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $line"
done

重定向

cmd > >(logger.sh > stdout.log) 2> >(logger.sh > stderr.log)
ktecyv1j

ktecyv1j3#

这是可行的,但我的命令必须在后台运行,因为它在一个init脚本中,因此我必须这样做:

({ cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log) &
echo $! > "$pid_file"

是吧?
但我认为在这种情况下,$pid_file中的pid不是$cmd的pid.

vsaztqbk

vsaztqbk4#

这是最简单的方法,我完成这一点与时间戳包括:

{ ./script.sh | ts >>script-stdout.log; } 2>&1 | ts >>script-stderr.log

相关问题