7

Given the following test script:

#!/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 

If the line "does not work" is enabled and the script is run and then Ctrl+C is pressed; script is ended immediately, without executing the traps.

If "redirect output to stdout" part is removed (the "works as expected" line is enabled) and then Ctrl+C is pressed, the traps are executed.

Why is that?

10

The other answer advises exec &> /dev/tty, so traps write to /dev/tty regardless of previous redirections:

The traps are run, but the standard output redirect to /dev/null is still in place, so the output is not printed. […] add exec &> /dev/tty to work around it by re-establishing the connection from standard output/error to the terminal.

Sometimes this may not be the best solution. Consider a general case when you want your script ("fixed" with exec &> /dev/tty) to be silent. You invoke

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

but then the trap is triggered and it writes to /dev/tty anyway.

For me a better way is to store the original stdout and stderr in a form of "backup" file descriptors:

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

Then, inside the trap function, you either redirect any single command:

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

or restore the original destinations and proceed as usual:

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

This way the redirections applied to the interrupted command won't affect the trap; still the redirections applied to the entire script will.

  • Accepted as this is a more general solution. Thanks. – ceremcem Jun 10 at 5:48
8

The traps are run, but the standard output redirect to /dev/null is still in place, so the output is not printed. Try replacing the trap contents with touch "$FUNCNAME" to verify, or add exec &> /dev/tty to work around it by re-establishing the connection from standard output/error to the terminal. As to why, this may be part of a larger feature to keep a lot of the original environment when running traps in order to avoid surprises.

  • 1
    Adding exec &> /dev/tty to the first line of pre_cleanup function body fixed the issue. Thanks. – ceremcem Jun 9 at 6:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.