如何将 stdout 重定向到一个文件,并将 stdout+stderr 重定向到另一个文件?

如何将 stdout 重定向到一个文件,并将 stdout+stderr 重定向到另一个文件?

我怎样才能实现

cmd >> file1 2>&1 1>>file2

也就是说,标准输出stderr 应该重定向到一个文件(文件 1),而只有 stdout(文件 2)应该重定向到另一个文件(都处于附加模式)?

答案1

问题是,当您重定向输出时,它不再可用于下一个重定向。您可以tee在子 shell 中使用管道来保留第二次重定向的输出:

( cmd | tee -a file2 ) >> file1 2>&1

或者如果您想在终端中查看输出:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

为了避免将第一个的 stderr 添加到teefile1您应该将命令的 stderr 重定向到某个文件描述符(例如 3),然后再次将其添加到 stdout:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(感谢@fra-san)

答案2

zsh

cmd >& out+err.log > out.log

在追加模式下:

cmd >>& out+err.log >> out.log

在 中zsh,如果该mult_ios选项尚未禁用,则当文件描述符(此处为 1)被重定向多次以进行写入时,shell 会实现一个内置函数tee以将输出复制到所有目标。

答案3

您可以:标记stdout(使用未缓冲的sed,即sed -u ...:),让stderr也转到stdout(未标记,因为它没有经过该标记sed),从而能够区分生成的日志文件中的2。

如下: 是慢的(它可以被认真优化,例如使用 perl 脚本而不是 while ... ; do ... ;done,例如,这将在每一行生成子 shell 和命令!),诡异的(似乎我需要 2 个 {} 阶段在一个阶段重命名 stdout,然后在另一个阶段添加“失败”stderr),等等。但它是:一个“概念证明",这将尝试尽可能保持 stdout 和 stderr 的输出顺序:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file

答案4

为了多样性:

如果您的系统支持/dev/stderr,那么

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

将工作。的标准输出cmd 被发送到管道的stdout 和stderr。标准错误cmd绕过tee 管道的 stderr。

所以

  • 管道的标准输出只是cmd,的标准输出
  • 管道的 stderr 是 , 的 stdout 和 stderr 的cmd混合。

然后将这些流发送到正确的文件就很简单了。

与几乎任何类似的方法一样(包括史蒂芬的回答), file1可能会使线路乱序。

相关内容