bash重定向到同一文件的一致性

时间:2015-03-27 08:01:20

标签: linux bash shell

我想将某些命令的stdout和stderr重定向到作为参数传递给我的脚本的文件。参数是可选的,默认情况下它们是/dev/stdout/dev/stderr

这就是我以下列方式some-command 1>$myStdout 2>$myStderr重定向的原因。由于我事先并不知道myStdoutmyStderr是否相同,我无法使用2>&1这种重定向方式。

这就是问题所在。如果myStdoutmyStderr指向同一个文件,我可能会丢失一些输出。请考虑以下脚本

>&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

但这种方式很难看。我必须以某种方式检查文件是否相同(并处理符号链接解决),我必须复制相当大的命令。

3 个答案:

答案 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重定向则是第二次截断,因此只在该文件中留下一个输出。