我想将某些命令的stdout和stderr重定向到作为参数传递给我的脚本的文件。参数是可选的,默认情况下它们是/dev/stdout
和/dev/stderr
。
这就是我以下列方式some-command 1>$myStdout 2>$myStderr
重定向的原因。由于我事先并不知道myStdout
和myStderr
是否相同,我无法使用2>&1
这种重定向方式。
这就是问题所在。如果myStdout
和myStderr
指向同一个文件,我可能会丢失一些输出。请考虑以下脚本
>&2 echo "err" &
echo "out"
如果我像test.sh 1>log 2>&1
那样运行它,那么我会在日志文件中看到这两行,但如果我像test.sh 1>log 2>log
那样运行它,那么我只看到err
。
这是第一个问题:为什么会发生这种情况? bash如何处理上述示例中的并发写入?
我该如何解决?我知道我可以检查文件是否相同
if [[ $myStdout and $myStderr are the same ]]; then
redirect using 1>$myStdout 2>&1
else
redirect using 1>$myStdout 2>$myStderr
fi
但这种方式很难看。我必须以某种方式检查文件是否相同(并处理符号链接解决),我必须复制相当大的命令。
答案 0 :(得分:2)
如果单独打开描述符,它们就是完全独立的流。如果他们指向同一个文件,他们每个人都会独立跟踪文件偏移量。因此,当您第一次写入一个流时,它将写入文件的开头,第一次写入另一个流时,它也会写入文件的开头,覆盖先前写入的内容。这将继续发生,每个流的写入都会覆盖另一个。
您可以做的是首先清空每个文件,然后以追加模式打开它们:
> $myStdout
> $myStderr
redirect 1>> $myStdout 2>> $myStderr
通过附加,每个写入将首先搜索到文件的末尾,因此它将在之前的写入之后执行,而不是覆盖它。
然而,可能还有另一个问题。如果程序使用stdio,则默认情况下会缓存stdout
,但不会stderr
。因此,除非程序在打印错误消息之前刷新stdout
缓冲区,否则它们可能会散布在输出中的随机位置,而不是整齐地放在它们自己的行上。
答案 1 :(得分:1)
我们来解释一下:
test.sh 1>log 2>&1
:将STDERR重定向到STDOUT,然后将所有STDOUT重定向到log
test.sh 1>log 2>log
:将STDERR重定向到log
,将STDOUT重定向到log
不同之处在于,在第一个示例中,shell只保存一个文件处理程序log
,而它在第二个示例中打开了2个文件处理程序。
在开始执行之前,您应该做truncate
输出文件,然后使用追加重定向>>
而不是正常的>
它在打开时截断输出文件。
答案 2 :(得分:0)
您可以使用一个覆盖和一个追加模式重定向来解决它:
bash test.sh >>"$myStdout" 2>"$myStderr"
如果你只是在stdout和stderr中使用>
和相同的文件,那么shell会为stderr重定向截断你的文件两次,而对stdout重定向则是第二次截断,因此只在该文件中留下一个输出。