当我遇到一个有趣的结果时,我正在忙于捕获退出代码、重定向stdout
和stderr
案例声明,我希望有人能够阐明这一点。
我使用的是 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 1
主case
语句会导致cleanup
函数继承该语句的重定向。
一个较短的示例,不将任何内容打印到标准输出:
cleanup () {
echo bye
}
trap cleanup EXIT
exit >/dev/null
删除>/dev/null
即可获得脚本输出bye
。
产生的跟踪输出set -x
被写入标准错误流,并且您的代码/dev/null
也将该流重定向到。简而言之,你的函数的case
声明是已执行,但您会丢弃脚本的所有输出,因此您不会看到其输出echo
,也不会看到跟踪输出。
另请注意,其他一些 shell(例如dash
和ksh
)不会以这种方式运行。
要在 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