如果输出重定向到标准输出,为什么 Bash trap 不起作用?

如果输出重定向到标准输出,为什么 Bash trap 不起作用?

给出以下测试脚本:

#!/bin/bash

# see "killing timeout": https://unix.stackexchange.com/a/57692/65781
declare -a timeout_pids
my_timeout(){
    local args tp
    args="$@"
    timeout $args &
    tp=$!
    #echo "pid of timeout: $tp"
    timeout_pids+=($tp)
    wait $tp
}

cleanup(){
    echo "-----------------------------------------"
    echo "Restoring previous routing table settings"
}

pre_cleanup(){
    echo "Executing pre-cleanup..."
    exit
}

trap pre_cleanup INT
trap cleanup EXIT

echo "ctrl+c now to execute cleanup"
#my_timeout 9s sleep 20 2> /dev/null >/dev/null # <- does not work as expected!
my_timeout 9s sleep 20 2> /dev/null # <- works as expected 

如果启用了“不起作用”行并且脚本运行然后Ctrl+C按下;脚本将立即结束,而不执行陷阱。

如果删除“将输出重定向到标准输出”部分(启用“按预期工作”行)然后Ctrl+C按下,则会执行陷阱。

这是为什么?

答案1

其他答案建议,因此无论先前的重定向如何,exec &> /dev/tty陷阱都会写入:/dev/tty

陷阱已运行,但标准输出重定向到的/dev/null仍然有效,因此不会打印输出。[…]exec &> /dev/tty通过重新建立从标准输出/错误到终端的连接来解决此问题。

有时这可能不是最好的解决方案。考虑一种一般情况,当你希望你的脚本(用 固定exec &> /dev/tty)保持沉默时。你调用

./the_script >/dev/null 2>/dev/null

但随后陷阱被触发并且它/dev/tty仍然会进行写入。

对我来说,更好的方法是将原始 stdout 和 stderr 以“备份”文件描述符的形式存储:

# at the very beginning of the script
exec 21>&1
exec 22>&2

然后,在陷阱函数内部,您可以重定向任何单个命令:

echo "Some output" >&21
echo "Some error" >&22

或恢复原始目的地并照常进行:

# at the beginning of the trap function
exec 1>&21
exec 2>&22

这样,应用于中断命令的重定向将不会影响陷阱;但应用于整个脚本的重定向仍然会。

答案2

陷阱运行,但标准输出重定向到 /dev/null 仍然有效,因此不会打印输出。尝试用 替换内容traptouch "$FUNCNAME"进行验证,或添加exec &> /dev/tty以通过重新建立从标准输出/错误到终端的连接来解决这个问题。至于原因,这可能是一个更大的功能的一部分,以便在运行陷阱时保留大量原始环境,以避免意外。

相关内容