在屏幕上仅显示stderr,但将stdout和stderr都写入文件


Answers:


14

即使没有任何重定向,或者什么也没有>logfile 2>&1,您也不保证可以按生成顺序查看输出。

对于初学者,来自应用程序的stdout将被行缓冲(到tty)或被缓冲(到管道),但是stderr是未缓冲的,因此就读者而言,输出顺序之间的关系被破坏了。您可以构想的任何管道中的后续阶段都将无法确定性地访问两个流(从概念上讲,它们是并行发生的,并且您始终受制于调度程序-如果在读者获得分片时,编写者已经写入两个管道,则无法确定先到者。

“它们发生的顺序”仅是应用程序真正知道的。在stdout / stderr上的输出排序是一个众所周知的问题,也许是经典问题。


7

当您使用的建设: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!

这样一来,您就无法将两个流记录到同一文件中。
artistoex 2011年

1
@artistoex:您只可以使用tee -a用于将同一文件中的1>两个输出求和的相同文件。像这样的东西:./test 2>&1 1>out+err.log | tee -a out+err.log
mmoya 2011年

我不知道为什么,但这wget -O - www.google.de对我不起作用。(与下面的解决方案比较)
artistoex 2011年

4
tee -a如果什么也没有重定向,使用将无法可靠地获得与控制台相同的输出。tee与直接来自命令的输出相比,通过的输出可能会稍有延迟,因此可能会稍后出现在日志中。
吉尔斯(Gilles)“所以,别再邪恶了”,

这对我不起作用,out + err.log为空。是的,我希望将stderror和standard放在同一文件中
Andreas

7

通过将以下行放在bash脚本的顶部,我已经能够实现这一点:

exec 1>>log 2> >(tee -a log >&2)

这将标准输出重定向到文件log1>>log),然后发球错误输出到文件log2> >(tee -a log),将它背到标准错误(>&2)。就这样,我得到一个单一的文件,log,其中显示,无论输出和错误为了和stderr也是照常显示在屏幕上。

问题是,仅当我附加到文件时,它才似乎起作用。如果我不追加,则似乎这两个重定向会相互干扰,并且我只会得到最后一个输出。


有什么办法可以“修改” STDERR行以包含自定义字符串,以便您可以轻松地在其上进行grep?
IMTheNachoMan

1

您想复制错误流,以使其同时出现在控制台和日志文件中。所需的工具是tee,所有您需要做的就是将其应用于错误流。不幸的是,没有标准的shell构造将一个命令的错误流通过管道传递到另一个命令中,因此需要一些文件描述符重新排列。

{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^   ^^^^^^^^^^^^    ^^^^^^^^^
  command produces output      stdout3                    log
  command produces error       stderr1   dup to terminal  log

这对我来说也不是很有效,首先获取stdout行,然后获取“ log”文件中的所有stderr行。范例:{{回显;echo err 1>&2; echo out2; echo err2 1>&2; } 2>&1>&3 | tee / dev / tty; }>日志3>&1
Andreas

1
@Andreas:哦,du,tee在这里也引入了延迟。我认为没有纯粹的shell解决方案。实际上,我想知道是否存在不以某种方式修改应用程序的解决方案。
吉尔(Gilles)“所以,别再邪恶了”,

1

要将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

希望能对有人提出帮助,他们在提出原始问题后很久才到达这里。


为了纯粹的+1,我有一个相似的想法(两个时间戳都通过perl进行时间戳记),但是正在努力实现它。我将研究它是否对我有用(即,如果它确实保持了输出顺序,那么由于stderr和stdout的标记之间的异步性,这里的其他解决方案会使情况有所改变)
Olivier Dulac

0

假设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 )

1
这几乎对我有用,但是在日志文件中,我首先获取所有stdout行,然后获取所有stderror行。我希望它们在发生时以自然顺序交错。
安德烈亚斯(Andreas)

0

鉴于我在这里所读的内容,我唯一能看到的解决方案是在每行(无论是STOUT还是STERR)前面都加一个时间片/时间戳。date +%s将花费几秒钟,但多数民众赞成在减缓维护秩序的过程中。此外,日志将以某种方式必须进行排序。工作量超出我的能力范围。


0

我一直在努力满足这个确切的要求,最终找不到简单的解决方案。我最终要做的是:

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

0

将stderr替换为command-替代tee -a,并将stdout附加到同一文件:

./script.sh 2> >(tee -a outputfile) >>outputfile

并注意:还要确保正确的顺序(但没有stderr-show),'expect'工具中有'unbuffer'命令,该命令模拟tty并使stdout / err保持在终端上显示的顺序:

unbuffer ./script.sh > outputfile
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.