【问题标题】:How to redirect and append both standard output and standard error to a file with Bash如何使用 Bash 将标准输出和标准错误重定向并附加到文件中
【发布时间】:2010-10-26 22:47:07
【问题描述】:

要将standard output 重定向到 Bash 中的截断文件,我知道要使用:

cmd > file.txt

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

cmd >> file.txt

要将标准输出和standard error 都重定向到截断的文件,我知道要使用:

cmd &> file.txt

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

【问题讨论】:

  • 我想指出 &>outfile 是 Bash(和其他)特定的代码,不可移植。便携的方式(类似于附加的答案)一直是并且仍然是 >outfile 2>&1
  • ... 排序很重要。
  • 这能回答你的问题吗? Redirect stderr and stdout in Bash
  • @BrettHale 我将其标记为这个问题的副本,主要是因为这里接受的答案可以移植到其他 shell,并且这个问题可以更好地表达。奇怪的是同一个用户问了两次同一个问题,直到现在才被注意到。

标签: bash append stdout io-redirection stderr


【解决方案1】:
cmd >>file.txt 2>&1

Bash 从左到右执行重定向如下:

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

【讨论】:

  • 效果很好!但是有没有办法理解这一点,或者我应该把它当作一个原子 bash 构造来对待?
  • 这是简单的重定向,重定向语句的评估一如既往,从左到右。 >>文件:红色。 STDOUT 到文件(追加模式)(1>>file 的缩写)2>&1:红色。 STDERR 到“stdout 的去向”请注意,“将 STDERR 重定向到 STDOUT”的解释是错误的。
  • 它说“将输出(stdout,文件描述符1)附加到file.txt并将stderr(文件描述符2)发送到与fd1相同的位置”。
  • @TheBonsai 但是,如果我需要将 STDERR 重定向到另一个文件但要追加怎么办?这可能吗?
  • 如果你做cmd >>file1 2>>file2它应该达到你想要的。
【解决方案2】:

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

经典且可移植的(Bash pre-4)方式是:

cmd >> outfile 2>&1

一种不可移植的方式,从 Bash 4 开始是

cmd &>> outfile

(类似于&> outfile

为了良好的编码风格,你应该

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

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

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

语法(除了其他重定向语法)在Bash hackers wiki 中描述。

【讨论】:

  • 我更喜欢 &>>,因为它与 &> 和 >> 一致。阅读“将输出和错误附加到此文件”也比“将错误发送到输出,将输出附加到此文件”更容易。请注意,虽然 Linux 通常具有当前版本的 bash,但 OS X 在撰写本文时仍需要通过自制软件等手动安装 bash 4。
  • 我更喜欢它,因为它更短,每行只有两个位置,那么例如 zsh 会用“&>>”做什么?
  • 另外需要注意的是,在 cron 作业中,您必须使用 pre-4 语法,即使您的系统有 Bash 4。
  • @zsero cron 根本不使用 bash...它使用 sh。您可以通过在 crontab -e 文件前添加 SHELL=/bin/bash 来更改默认 shell。
【解决方案3】:

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

cmd >log.out 2>log_error.out

附加将是:

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

【讨论】:

  • 使用您的第一个选项将两个流重定向到同一个文件将导致第一个流在第二个的“顶部”写入,覆盖部分或全部内容。请改用 cmd >> log.out 2> log.out
  • 感谢您的关注;你是对的,一个会破坏另一个。但是,您的命令也不起作用。我认为写入同一文件的唯一方法是在cmd >log.out 2>&1 之前给出。我正在编辑我的答案以删除第一个示例。
  • cmd > my.log 2> my.log 不起作用的原因是重定向是从左到右评估的,> my.log 说“创建新文件my.log 替换现有文件并将stdout 重定向到该文件" 并且 已经完成之后,评估 2> my.log 并显示“创建新文件 my.log 替换现有文件并将 stderr 重定向到该文件”。由于 UNIX 允许删除打开的文件,标准输出现在记录到以前称为 my.log 但后来被删除的文件中。关闭该文件的最后一个文件句柄后,文件 contents 也将被删除。
  • 另一方面,cmd > my.log 2>&1 有效,因为> my.log 说“创建新文件my.log 替换现有文件并将stdout 重定向到该文件”,之后已经完成, 2>&1 表示“将文件句柄 2 指向文件句柄 1”。根据 POSIX 规则,文件句柄 1 始终是标准输出,2 始终是标准错误,因此 stderr 然后从第一次重定向指向已打开的文件 my.log。请注意语法>& 不会创建或修改实际文件,因此不需要>>&。 (如果 first 重定向是 >> my.log 则文件只是以附加模式打开。)
【解决方案4】:

这应该可以正常工作:

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

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

【讨论】:

  • 如果您也想在终端中查看输出,这也是正确的答案。然而,这不是最初提出的问题。
【解决方案5】:

在 Bash 4(以及 Z shell (zsh) 4.3.11)中:

cmd &>> outfile

开箱即用。

【讨论】:

  • @all:这是一个很好的答案,因为它适用于 bash 并且很简短,所以我进行了编辑以确保它明确提及 bash。
  • @mikemaccana: TheBonsai's answer 显示自 2009 年以来的 bash 4 解决方案
  • 为什么当它包含在 TheBonsai 的答案中时,它甚至存在?请考虑删除它。你会得到一个disciplined badge
【解决方案6】:

试试这个:

You_command 1> output.log  2>&1

您对 &> x.file 的使用在 Bash 4 中确实有效。对此感到抱歉:(

这里有一些额外的提示。

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

0 代表standard input,1 代表standard output,2 代表standard error。 3~9 留作其他临时用途。

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

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

请参阅 Chapter 20. I/O Redirection 中的参考资料。

【讨论】:

  • 您的示例将执行与 OP 要求不同的操作:它将You_command 的标准错误重定向到标准输出,并将You_command 的标准输出重定向到文件output.log。此外,它不会附加到文件中,但会覆盖它。
  • 正确:文件描述符可以是所有其他文件大于 3 的任何值。
  • 您的回答显示了最常见的输出重定向错误:将 STDERR 重定向到 STDOUT 当前指向的位置,并且仅在将 STDOUT 重定向到文件之后。这不会导致 STDERR 被重定向到同一个文件。重定向的顺序很重要。
  • 这是否意味着,我应该首先将 STDERROR 重定向到 STDOUT,然后将 STDOUT 重定向到文件。 1 > output.log 2>&1
  • @Quintus.Zhou Yup。您的版本将 err 重定向到 out,同时将 out 重定向到文件。
【解决方案7】:

另一种方法:

如果使用 &>> 不可用的旧版本 Bash,您也可以这样做:

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

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

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

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

为避免启动子shell,您可以使用花括号代替括号来创建组命令

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

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

【讨论】:

  • 此实现会导致系统运行一个额外的进程。使用语法 cmd >> file 2>&1 可以在所有 shell 中使用,并且不需要额外的进程来运行。
  • @MikkoRantalainen 我已经解释过它会产生一个子shell并且效率较低。这种方法的重点是,如果效率不是什么大问题(而且很少如此),那么这种方法更容易记住,也更难出错。
  • @MikkoRantalainen 我已经用一个避免产生子外壳的变体更新了我的答案。
  • 如果您真的不记得语法是cmd >> file 2>&1 还是cmd 2>&1 >> file,我认为使用cmd 2>&1 | cat >> file 而不是使用大括号或括号会更容易。对我来说,一旦您了解cmd >> file 2>&1 的实现实际上是“将STDOUT 重定向到file”,然后是“将STDERR 重定向到STDOUT 当前指向的任何文件”(显然是@987654333 @ 在第一次重定向之后),很明显您放置重定向的顺序。 UNIX 不支持重定向到流,只支持流指向的 file 描述符。
【解决方案8】:

脚本本身的重定向

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

#!/bin/bash

exec 1>>logfile.txt
exec 2>&1

/bin/ls -ld /tmp /tnt

运行这将创建/附加logfile.txt,包含:

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

记录到许多不同的文件

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

#!/bin/bash

if [ -e last.log ] ;then
    mv -f last.log last.old
fi
exec 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:

如果你打算在 interactive 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.old
exec 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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-12
  • 2020-11-05
  • 1970-01-01
相关资源
最近更新 更多