【问题标题】:Write STDOUT & STDERR to a logfile, also write STDERR to screen将 STDOUT 和 STDERR 写入日志文件,同时将 STDERR 写入屏幕
【发布时间】:2010-05-20 05:30:58
【问题描述】:

我想运行几个命令,并将所有输出捕获到一个日志文件中。我还想将任何错误打印到屏幕上(或者选择将输出邮寄给某人)。

这是一个例子。以下命令将运行三个命令,并将所有输出(STDOUT 和 STDERR)写入单个日志文件。

{ command1 && command2 && command3 ; } > logfile.log 2>&1

这是我想要对这些命令的输出执行的操作:

  • 所有命令的 STDERR 和 STDOUT 都保存在一个日志文件中,以防我以后需要它——除非有问题,否则我通常不会在这里查看。
  • 将 STDERR 打印到屏幕上(或可选地,通过管道打印到 /bin/mail),这样任何错误都不会被忽略。
  • 如果返回码仍然可用,那就太好了,这样我就可以进行一些错误处理。如果出现错误,也许我想发送电子邮件,如下所示:

    { 命令1 && 命令2 && 命令3 ; } > 日志文件.log 2>&1 || mailx -s "出现错误" stefanl@example.org

我遇到的问题是 STDERR 在 I/O 重定向期间丢失了上下文。 '2>&1' 会将 STDERR 转换为 STDOUT,因此如果我这样做,我将无法查看错误 2> error.log

这里有几个更有趣的例子。假设我正在运行一些熟悉的构建命令,但我不希望整个构建仅仅因为一个错误而停止,所以我使用了“--keep-going”标志。

{ ./configure && make --keep-going && make install ; } > build.log 2>&1

或者,这是一个简单的(可能是草率的)构建和部署脚本,它会在发生错误时继续运行。

{ ./configure && make --keep-going && make install && rsync -av --keep-going /foo devhost:/foo} > build-and-deploy.log 2>&1

我想我想要的是某种Bash I/O Redirection,但我想不通。

【问题讨论】:

    标签: bash unix shell


    【解决方案1】:
    (./doit >> log) 2>&1 | tee -a log
    

    这将获取标准输出并将其附加到日志文件中。

    然后标准错误将被转换为标准输出,该标准输出通过管道传输到tee,后者将其附加到日志中(如果您有 Bash 4,则可以将2>&1 | 替换为|&)并将其发送到标准输出,这将要么出现在 tty 上,要么可以通过管道传送到另一个命令。

    我对两者都使用了附加模式,因此无论外壳重定向和tee 打开文件的顺序如何,您都不会吹走原始文件。也就是说,stderr/stdout 可能以意想不到的方式交错。

    【讨论】:

    • 您的代码可以工作,但只要我运行多个命令(如( ./doit && ./doit2 >> log ) 2>&1 | tee -a log),doit 和 doit2 的 stdout 和 stderr 就会打印到屏幕上。
    • 如果我这样做似乎可行:( { ./doit && ./stdout.sh ;} >> log) 2>&1 | tee -a log
    • @StefanLasiewski - 是的,文件重定向仅适用于它之前的命令。将这两个命令组合到一个子 shell 中的解决方法是最简单的解决方法;另一个更丑陋的解决方法是为每个命令重定向标准输出( ./doit >> log && ./doit2 >> log ) 2>&1 | tee -a log
    • 令人震惊的是,这样的事情似乎也有效:{ { date && ./doit && ./stdout.sh ; } >> log ; } 2>&1 | tee -a log
    • 这不会保留输出的顺序。在我的简单测试用例中,log 包含首先打印到 stdout 的所有行,然后是打印到 stderr 的行;而程序交替打印它们。
    【解决方案2】:

    如果您的系统有 /dev/fd/* 节点,您可以这样做:

    ( exec 5>logfile.txt ; { command1 && command2 && command3 ;} 2>&1 >&5 | tee /dev/fd/5 )
    

    这将打开文件描述符 5 到您的日志文件。执行将标准错误定向到标准输出的命令,将标准输出定向到 fd 5 并将 stdout(现在仅包含 stderr)通过管道传输到 tee,从而将输出复制到作为日志文件的 fd 5。

    【讨论】:

    • 当我执行你的命令时,我的日志文件只包含我在屏幕上看到的错误消息。标准输出丢失。
    • 这也有效。但是,我不确定我明白为什么。但是我尝试了这样的命令,它可以工作:( exec 5>logfile.txt ; { date && ./doit && ./stdout.sh ;} 2>&1 >&5 | tee /dev/fd/5 )
    【解决方案3】:

    这里是如何运行一个或多个命令,捕获标准输出和错误,按照它们生成的顺序,到日志文件,并在任何终端屏幕上只显示标准错误你喜欢。在 linux 上的 bash 中工作。可能适用于大多数其他环境。我将用一个例子来说明它是如何完成的。

    预赛:

    打开两个窗口(shell、tmux 会话等)

    我会用一些测试文件来演示,所以创建测试文件:

    touch /tmp/foo /tmp/foo1 /tmp/foo2
    

    在窗口 1:

    mkfifo /tmp/fifo
    
    0</tmp/fifo cat - >/tmp/logfile
    

    然后,在window2中:

    (ls -l /tmp/foo /tmp/nofile /tmp/foo1 /tmp/nofile /tmp/nofile; echo successful test; ls /tmp/nofile1111) 2>&1 1>/tmp/fifo | tee /tmp/fifo 1>/dev/pts/2
    

    /dev/pts/2 替换为您希望stderr 显示的任何tty。

    子shell中各种成功和不成功命令的原因只是为了生成混合的输出和错误消息流,以便您可以在日志文件中验证正确的顺序。了解它的工作原理后,将“ls”和“echo”命令替换为您选择的脚本或命令。

    使用这种方法,输出和错误的顺序被保留,语法简单明了,并且只有一个对输出文件的引用。此外,您可以灵活地将额外的 stderr 副本放在任何您想要的位置。

    【讨论】:

    • 这与上面的 R Samuel Klatchkos 回复基本相同。它也不保留排序。尝试使用一些简单的脚本 à la echo out 1; echo err 2 &gt;&amp;2; echo out 3; echo err 4 &gt;&amp;2 而不是你的 ls 东西,你会看到的。我假设您的示例有效,因为在 ls 启动时, tee 获得了一些 CPU 周期,但这只是运气,在某些现实世界中这不起作用。
    • 我明天再测试一次,但我记得我第一次已经做了一些严格的测试,包括 echo 和其他命令,结果一致。
    【解决方案4】:

    试试:

    command 2>&1 | tee output.txt
    

    此外,您可以将 stdout 和 stderr 指向不同的位置:

    command > stdout.txt >& stderr.txt
    
    command > stdout.txt |& program_for_stderr
    

    所以上述的一些组合应该适合你——例如您可以将 stdout 保存到一个文件中,并将 stderr 保存到一个文件中并管道到另一个程序(使用 tee)。

    【讨论】:

    • 您的第一行可以将 stdout 和 stderr 发送到终端和文件,但后两行无法将它们发送到不同的地方。 &gt;stdout.txt 最终无效,因为当您使用 &gt;&amp;|&amp; 时,它会重定向 both 流。
    • 第一个和第三个命令不起作用,因为stderr被2&gt;&amp;1重定向到stdout。这就是我上面所说的“STDERR 在 I/O 重定向期间丢失上下文”的意思。 Bash 参考手册说|&amp;“是2&gt;&amp;1 | 的简写。第二个命令不会将stderr 打印到屏幕上。
    【解决方案5】:

    在脚本的开头添加这个

    #!/bin/bash
    set -e 
    outfile=logfile
    
    exec > >(cat >> $outfile)
    exec 2> >(tee -a $outfile >&2)
    
    # write your code here
    

    STDOUTSTDERR 将被写入$outfile,控制台上只会看到STDERR

    【讨论】:

      猜你喜欢
      • 2019-12-11
      • 1970-01-01
      • 2017-07-04
      • 2019-08-21
      • 1970-01-01
      • 1970-01-01
      • 2016-11-03
      • 1970-01-01
      • 2016-04-21
      相关资源
      最近更新 更多