![如何将 stdout 重定向到一个文件,并将 stdout+stderr 重定向到另一个文件?](https://linux22.com/image/153512/%E5%A6%82%E4%BD%95%E5%B0%86%20stdout%20%E9%87%8D%E5%AE%9A%E5%90%91%E5%88%B0%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%EF%BC%8C%E5%B9%B6%E5%B0%86%20stdout%2Bstderr%20%E9%87%8D%E5%AE%9A%E5%90%91%E5%88%B0%E5%8F%A6%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%EF%BC%9F.png)
我怎样才能实现
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 添加到tee
,file1
您应该将命令的 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
可能会使线路乱序。