Bash 重定向奇怪的行为

Bash 重定向奇怪的行为

当我遇到一个有趣的结果时,我正在忙于捕获退出代码、重定向stdoutstderr案例声明,我希望有人能够阐明这一点。

我使用的是 bash 5.1.16。

该块可用于复制我所看到的行为:

#!/bin/sh -x

cleanup () {
    case $? in
        1) echo "message 1" ;;
        9) echo "message 2" ;;
    esac
}

trap cleanup EXIT

case 0 in
    0) exit 1 ;;
    2) echo "yay" ;;
esac > /dev/null 2>&1

xtrace 输出> /dev/null 2>&1

+ trap cleanup EXIT

xtrace 输出不带> /dev/null 2>&1

+ trap cleanup EXIT
+ case 0 in
+ exit 1
+ cleanup
+ case $? in
+ echo 'message 1'
message 1

这里发生了什么?重定向如何导致 case 语句根本不执行?

答案1

陷阱EXIT是通过调用陷阱时的重定向来执行的。在代码中,调用exit 1case语句会导致cleanup函数继承该语句的重定向。

一个较短的示例,不将任何内容打印到标准输出:

cleanup () {
        echo bye
}

trap cleanup EXIT

exit >/dev/null

删除>/dev/null即可获得脚本输出bye

产生的跟踪输出set -x被写入标准错误流,并且您的代码/dev/null也将该流重定向到。简而言之,你的函数的case声明已执行,但您会丢弃脚本的所有输出,因此您不会看到其输出echo,也不会看到跟踪输出。

另请注意,其他一些 shell(例如dashksh)不会以这种方式运行。

要在 shell 中解决此问题bash,您可以在脚本开头复制标准错误文件描述符,然后在函数中显式使用它cleanup。我为此使用标准错误流,因为我假设它将用于诊断消息。

你的代码加上我的补充:

#!/bin/bash -x

exec {fd}>&2

cleanup () {
    case $? in
        1) echo "message 1" ;;
        9) echo "message 2" ;;
    esac >&$fd
}

trap cleanup EXIT

case 0 in
    0) exit 1 ;;
    2) echo "yay" ;;
esac > /dev/null 2>&1

由 shell 分配并分配给的描述符fd将为 10 或更高。

由于sh并不总是bash,我还更改了#!-line 以显式调用bash可执行文件。

运行这个:

$ ./script
+ exec
+ trap cleanup EXIT
message 1

相关内容