Answers:
当您使用的建设:1>stdout.log 2>&1
两个标准错误和标准输出重定向到文件,因为该标准输出重定向在之前设置标准错误重定向。
如果您反转顺序,则可以将stdout重定向到文件,然后将stderr复制到stdout,以便可以将其通过管道传输到tee
。
$ cat test
#!/bin/sh
echo OUT! >&1
echo ERR! >&2
$ ./test 2>&1 1>stdout.log | tee stderr.log
ERR!
$ cat stdout.log
OUT!
$ cat stderr.log
ERR!
tee -a
用于将同一文件中的1>
两个输出求和的相同文件。像这样的东西:./test 2>&1 1>out+err.log | tee -a out+err.log
wget -O - www.google.de
对我不起作用。(与下面的解决方案比较)
tee -a
如果什么也没有重定向,使用将无法可靠地获得与控制台相同的输出。tee
与直接来自命令的输出相比,通过的输出可能会稍有延迟,因此可能会稍后出现在日志中。
通过将以下行放在bash脚本的顶部,我已经能够实现这一点:
exec 1>>log 2> >(tee -a log >&2)
这将标准输出重定向到文件log
(1>>log
),然后发球错误输出到文件log
(2> >(tee -a log
),将它背到标准错误(>&2)
。就这样,我得到一个单一的文件,log
,其中显示,无论输出和错误为了和stderr也是照常显示在屏幕上。
问题是,仅当我附加到文件时,它才似乎起作用。如果我不追加,则似乎这两个重定向会相互干扰,并且我只会得到最后一个输出。
您想复制错误流,以使其同时出现在控制台和日志文件中。所需的工具是tee
,所有您需要做的就是将其应用于错误流。不幸的是,没有标准的shell构造将一个命令的错误流通过管道传递到另一个命令中,因此需要一些文件描述符重新排列。
{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^
command produces output stdout→3 →log
command produces error stderr→1 dup to terminal →log
tee
在这里也引入了延迟。我认为没有纯粹的shell解决方案。实际上,我想知道是否存在不以某种方式修改应用程序的解决方案。
要将stderr写入屏幕并将stderr和stdout都写入文件-并以与将stderr和stdout的行都写入屏幕的顺序相同的顺序输出:
原来这是一个棘手的问题,尤其是如果您只是将它们写到屏幕上,就会遇到“序列相同”的问题。用简单的术语来说:将每个都写到自己的文件中,做一些后台处理魔术以标记每个行(在每个文件中)的确切时间,然后标记:然后,“ tail --follow” stderr文件到屏幕,但要同时看到“ stderr”和“ stdout”这两个文件(按顺序),将两个文件(每行上都带有精确时间标记)排序在一起。
码:
# Set the location of output and the "first name" of the log file(s)
pth=$HOME
ffn=my_log_filename_with_no_extension
date >>$pth/$ffn.out
date >>$pth/$ffn.err
# Start background processes to handle 2 files, by rewriting each one line-by-line as each line is added, putting a label at front of line
tail -f $pth/$ffn.out | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|1|".$_' >>$pth/$ffn.out.txt &
tail -f $pth/$ffn.err | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|2|".$_' >>$pth/$ffn.err.txt &
sleep 1
# Remember the process id of each of 2 background processes
export idout=`ps -ef | grep "tail -f $pth/$ffn.out" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
export iderr=`ps -ef | grep "tail -f $pth/$ffn.err" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
# Run the command, sending stdout to one file, and stderr to a 2nd file
bash mycommand.sh 1>>$pth/$ffn.out 2>>$pth/$ffn.err
# Remember the exit code of the command
myexit=$?
# Kill the two background processes
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"idout"}/'
echo kill $idout
kill $idout
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"iderr"}/'
echo kill $iderr
kill $iderr
date
echo "Exit code: $myexit for '$listname', list item# '$ix', bookcode '$bookcode'"
是的,这似乎很复杂,它会产生4个输出文件(您可以删除其中2个)。看来这是一个很难解决的问题,因此它采用了多种机制。
最后,按照您期望的顺序查看stdout和stderr的结果,请运行以下命令:
cat $pth/$ffn.out.txt $pth/$ffn.err.txt | sort
序列至少与仅将stdout和stderr转到屏幕时的序列非常接近的唯一原因是:每行都标记了一个毫秒以下的时间戳。
要在执行过程中在屏幕上看到stderr,请使用以下命令:
tail -f $pth/$ffn.out
希望能对有人提出帮助,他们在提出原始问题后很久才到达这里。
假设f
您要执行的命令是
( exec 3>/tmp/log; f 2>&1 1>&3 |tee >(cat)>&3 )
应该给你你想要的。例如wget -O - www.google.de
,如下所示:
( exec 3>/tmp/googlelog; wget -O - www.google.de 2>&1 1>&3 |tee >(cat)>&3 )
我一直在努力满足这个确切的要求,最终找不到简单的解决方案。我最终要做的是:
TMPFILE=/tmp/$$.log
myCommand > $TMPFILE 2>&1
[ $? != 0 ] && cat $TMPFILE
date >> myCommand.log
cat $TMPFILE >> myCommand.log
rm -f $TMPFILE
它以正确的交错顺序将stdout和stderr附加到日志文件。并且,如果发生任何错误(由命令的退出状态确定),它将以正确的交错顺序将整个输出(stdout 和 stderr)发送到stdout。我发现这是对我的需求的合理折衷。如果您想要单运行日志文件,而不是不断增长的多运行日志文件,则它甚至更简单:
myCommand > myCommand.log 2>&1
[ $? != 0 ] && cat myCommand.log
将stderr替换为command-替代tee -a
,并将stdout附加到同一文件:
./script.sh 2> >(tee -a outputfile) >>outputfile
并注意:还要确保正确的顺序(但没有stderr-show),'expect'工具中有'unbuffer'命令,该命令模拟tty并使stdout / err保持在终端上显示的顺序:
unbuffer ./script.sh > outputfile