如何使用Bash将标准输出和标准错误重定向并附加到文件中

要将标准输出重定向到Bash中的截断文件,我知道使用:

cmd > file.txt

要在Bash中重定向标准输出,附加到文件中,我知道使用:

cmd >> file.txt

要将标准输出和标准误差重定向到截断文件,我知道使用:

cmd &> file.txt

如何重定向附加到文件的标准输出和标准错误?cmd &>> file.txt对我不起作用。

920122 次浏览
cmd >>file.txt 2>&1

Bash执行从左到右的重定向,如下所示:

  1. >>file.txt:在追加模式下打开file.txt并将stdout重定向到那里。
  2. 2>&1:重定向stderr“#2现在去哪里”。在这种情况下,这是一个以追加模式打开的文件。换句话说,&1重用了stdout当前使用的文件描述符。

有两种方法可以做到这一点,具体取决于您的Bash版本。

经典和便携(Bash pre-4)的方法是:

cmd >> outfile 2>&1

一个不可移植的方式,从bash4开始

cmd &>> outfile

(模拟到&> outfile

对于良好的编码风格,您应该

  • 确定是否需要考虑可移植性(然后使用经典方法)
  • 决定是否需要考虑Bash pre-4的可移植性(然后使用经典方式)
  • 无论您使用哪种语法,都不要在同一个脚本中更改它(混淆!)

如果您的脚本已经以#!/bin/sh开头(无论是否有意),那么Bash 4解决方案以及通常任何特定于Bash的代码都不是可行的方法。

还要记住,Bash 4&>>只是更短的语法——它没有引入任何新功能或类似的东西。

语法(除了其他重定向语法)在bash黑客wiki中描述。

在Bash 4(以及z shellzsh)4.3.11)中:

cmd &>> outfile

刚开箱。

在Bash中,您还可以显式指定重定向到不同的文件:

cmd >log.out 2>log_error.out

追加将是:

cmd >>log.out 2>>log_error.out

试试这个:

You_command 1> output.log  2>&1

您对&> x.file的使用在Bash 4中确实有效。对不起:(

这里有一些额外的提示。

0,1,2,…,9是bash中的文件描述符。

0代表标准输入,1代表标准输出,2代表标准误差。3~9可用于任何其他临时用途。

任何文件描述符都可以通过使用运算符>>>(追加)重定向到其他文件描述符或文件。

用法:<file_descriptor>><文件名|&file_descriptor>

请参阅第20章. I/O重定向中的引用。

这应该工作得很好:

your_command 2>&1 | tee -a file.txt

它将所有日志存储在file.txt中,并将它们转储到终端中。

另一种方法:

如果使用旧版本的Bash,其中&>>不可用,您还可以执行以下操作:

(cmd 2>&1) >> file.txt

这会产生一个子shell,因此它的效率低于传统的cmd >> file.txt 2>&1方法,因此它不适用于需要修改当前shell的命令(例如cdpushd),但这种方法对我来说更自然,更容易理解:

  1. 将标准错误重定向到标准输出。
  2. 通过附加到文件来重定向新标准输出。

此外,括号消除了顺序的任何歧义,特别是如果您想将标准输出和标准错误管道传输到另一个命令。

为了避免启动子shell,您可以使用花括号而不是括号来创建组命令

{ cmd 2>&1; } >> file.txt

(请注意,需要分号(或换行符)来终止组命令。)

重定向自脚本本身

您可以从脚本本身计划重定向:

#!/bin/bash
exec 1>>logfile.txtexec 2>&1
/bin/ls -ld /tmp /tnt

运行它将创建/appendlogfile.txt,包含:

/bin/ls: cannot access '/tnt': No such file or directorydrwxrwxrwt 2 root root 4096 Apr  5 11:20 /tmp

登录到许多不同的文件

您可以创建两个不同的日志文件,附加到一个总体日志并重新创建另一个最后日志:

#!/bin/bash
if [ -e last.log ] ;thenmv -f last.log last.oldfiexec 1> >(tee -a overall.log /dev/tty >last.log)exec 2>&1
ls -ld /tnt /tmp

运行此脚本将

  • 如果last.log已经存在,将它们重命名为last.old(如果它们存在,则覆盖last.old)。
  • 创建一个新的last.log
  • 将所有内容附加到overall.log
  • 输出到终端。

简单组合日志

#!/bin/bash
[ -e last.err ] && mv -f last.err lasterr.old[ -e last.log ] && mv -f last.log lastlog.old
exec 2> >(tee -a overall.err combined.log /dev/tty >last.err)exec 1> >(tee -a overall.log combined.log /dev/tty >last.log)
ls -ld /tnt /tmp

所以你有

  • last.log上次运行日志文件
  • last.err上次运行错误文件
  • lastlog.old上一个运行日志文件
  • lasterr.old上一个运行错误文件
  • overall.log附加的整体日志文件
  • overall.err附加的整体错误文件
  • combined.log附加了整体错误和日志组合文件。
  • 仍然输出到终端

对于交互式会话,使用stdbuf

如果你打算在互动 shell中使用它,你必须告诉tee不要缓冲他的输入/输出:

# Source this to multi-log your session[ -e last.err ] && mv -f last.err lasterr.old[ -e last.log ] && mv -f last.log lastlog.oldexec 2> >(exec stdbuf -i0 -o0 tee -a overall.err combined.log /dev/tty >last.err)exec 1> >(exec stdbuf -i0 -o0 tee -a overall.log combined.log /dev/tty >last.log)

来源这个,你可以试试:

ls -ld /tnt /tmp

关于execstdbuf命令的一些解释:

  • 使用$(...)<(...)运行forks是通过运行subshell完成的,它将在另一个subshellsubsubshell)中执行二进制文件exec命令告诉shell脚本中没有其他命令要运行,因此二进制stdbuf ... tee)将作为$(...)0执行,在$(...)1(无需保留更多内存来运行另一个子进程)。

    bash的手册页(man -P'less +/^\ *exec\ ' bash):

        exec [-cl] [-a name] [command [arguments]]If  command  is  specified,  it  replaces theshell.  No new process is created....

    这不是真正的需要,但减少了系统占用空间。

  • 来自stdbuf的手册页:

    NAMEstdbuf  -  Run COMMAND, with modified bufferingoperations for its standard streams.

    这将告诉系统对tee命令使用无缓冲I/O。因此,当一些输入到来时,所有产出将立即成为更新

如果您关心两个流的内容顺序,请参阅@ed-morton对类似问题的回答,这里