将 stderr 和 stdout 重定向到一个文件会导致错误吗?

将 stderr 和 stdout 重定向到一个文件会导致错误吗?

我正在编写一个脚本,该脚本将 files ( filename...) 作为参数,并将 stdout 和 stderr 重定向到 name 的文件filename.output。文件中包含命令,我只想 bash 一次(以防文件中的命令不应运行多次,即mkdirrmdir)。我发现,当迭代参数时,此命令有效:

bash $a &> "$(basename $a).output"

但是,我还想使用此命令的错误状态,以防.output由于某种原因无法创建:

if ! bash $a &> "$(basename $a).output"
    then
        echo >&2 "failed to create $(basename $a).output"
fi

当我这样做时,我的 if 语句总是评估为 true,这我认为是因为 stdout 或 stderr 需要重定向失败,而不是两者。我正在测试的命令很少会同时产生 stderr 和 stdout(即像这样的命令date会产生 stdout,但不会产生 stderr,因此会导致非零返回值,因为 stderr 无法重定向)。

我对代码无法正常运行的原因的分析是否正确?如果是,有没有办法分别检查 stdout 重定向和 stderr 重定向的失败,以便我准确显示.output未创建的时间?

编辑:我发现问题实际上在于组件bash "$a",因为任何时候$a都有一个带有产生错误的命令的文件,整个 if 语句的计算结果为 true。我想现在我的问题是,是否存在重定向 stdout 和 stderr 会导致我应该注意的错误的情况?

答案1

如果你这样做

if ! bash $a &> "$(basename $a).output"; then

如果重定向中出现错误,或者脚本运行但返回非零退出状态,则 if 语句的主分支将运行。该&> file运算符与 相同> file 2>&1,它会重定向 stdout 和 stderr,但由于只涉及一个文件,因此您很难找到一种方法来使其中一个重定向成功,而另一个重定向失败。由于创建文件的各种原因,例如权限问题、不存在的路径、磁盘已满,重定向可能会失败。

让我们创建一个测试脚本:这只会打印一些内容,如果给它一个参数,它会错误退出。

$ cat test.sh
#!/bin/bash
echo test script
if [ "$#" != 0 ]; then
    echo exiting with error
    exit 1
fi
$ chmod +x test.sh

在这里,重定向失败(因为./non-existing-dir/确实不存在):

$ if ! ./test.sh > ./non-existing-dir/test.output; then 
    echo "it failed (for some reason)"; fi
bash: ./non-existing-dir/test.output: No such file or directory
it failed (for some reason)

在这里,重定向成功,并且输出被收集到文件中,但脚本本身返回失败状态:

$ rm -f test.output
$ if ! ./test.sh 1 > ./test.output; then
    echo "it failed (for some reason)"; fi
it failed (for some reason)

$ cat test.output 
test script
exiting with error

您不能错过重定向中的错误;一个将导致命令以非零状态退出。但确切的值尚未定义并且取决于 shell,因此我们无法使用它来区分重定向失败和脚本本身失败。 POSIX Shell 命令语言定义在 2.8.2 命令的退出状态中表示:

如果命令在字扩展或重定向期间失败,则其退出状态应介于 1 到 125(含)之间。

Bash 的手册中也说了类似的事情3.7.1 简单命令扩展


现在,如果您确实想专门检查重定向中的错误,您可以这样做,你只需在运行程序之前单独打开重定向即可。例如,我们可以使用这样的脚本来运行我们的程序:

$ cat redir.sh 
#!/bin/sh

outfile="${1?please define output file}"
cmd="${2?please define command to run}"
shift 2

if ! exec 9> "$outfile"; then
    echo "error: cannot create redirection to '$outfile'"
    exit 1
fi

if ! "$cmd" "$@" >&9 2>&9; then
    echo "error: script '$cmd' failed"
    exit 1
fi
exec 9>&-         # close the output fd

现在,检测到失败的重定向如下:

$ bash redir.sh ./non-existing-dir/test.output ./test.sh
redir.sh: line 8: ./non-existing-dir/test.output: No such file or directory
error: cannot create redirection to './non-existing-dir/test.output'

脚本失败也是如此:

$ bash redir.sh ./test.output ./test.sh 1
error: script './test.sh' failed

相关内容