2

I expected the following script to print This is redirected to 'output'. when I press ctrl+c:

#!/bin/bash

trap_function(){
    trap '' EXIT INT TERM
    echo "This is redirected to 'output'."
    touch this_will_exist
}
trap trap_function EXIT INT TERM

eval "sleep 100" &> output

Instead, nothing is displayed, and the text goes to the file output. How can I escape the redirect from within trap_function and have the text displayed to the user?

echo "This is redirected to 'output'." > /dev/stdout does not have the desired effect.

I run GNU bash, version 4.3.48 in Ubuntu 16.04.5 LTS.

katosh
  • 372
  • 4
  • 12
  • It works correctly in the online bash and e.g. zsh but does not print when running locally in Ubuntu or WLS. – katosh Feb 04 '19 at 12:43
  • Och, you are right, sorry. Looks like eval does not inherit traps. You can replicate it online (or another issue) by adding kill signal before the sleep exists, like `( sleep 0.5; kill -s SIGINT $$; ) &`. – KamilCuk Feb 04 '19 at 12:47
  • The trap is executed. The file `this_will_exist` is created and the text will end up in the file `output`. – katosh Feb 04 '19 at 12:51
  • @KamilCuk Your are right: http://tpcg.io/IF6Dtp – katosh Feb 04 '19 at 12:54
  • It *is* writing to standard output; the question is, where is it inheriting standard output *from*? – chepner Feb 04 '19 at 13:59

2 Answers2

0

A workaround is to have eval run in a subshell:

#!/bin/bash

trap_function(){
    trap '' EXIT INT TERM
    echo "This is redirected to 'output'."
    touch this_will_exist
}
trap trap_function EXIT INT TERM

(eval "sleep 100") &> output
katosh
  • 372
  • 4
  • 12
0

To understand what's happening one must know, that in order for bash to print anything that is send to stdout (which is the file descriptor 1) it sets the file descriptor 1 to the current terminal. This is nicely explained here https://catonmat.net/bash-one-liners-explained-part-three.

&> output replaces the terminal in file descriptor 1 and 2 with the file output. So there is no file descriptor left, to which echo "..." could send that would be displayed in the terminal. Hence, we have two possible solutions:

Redirecting to the current terminal

This works with the utility tty that returns the current user terminal.

#!/bin/bash

trap_function(){
    trap '' EXIT INT TERM
    echo "This is redirected to 'output'." &>$(tty)
    touch this_will_exist
}
trap trap_function EXIT INT TERM

eval "sleep 100" &> output

However, it may not be possible to capture this output with a wrapper.

Redirecting over another file descriptor

This solution requires that an additional file descriptor is set to the current terminal (e.g., 123). We can simply use what is already set in the file descriptor 1 before we replace it with output by 123>&1. Then we can set the file descriptor of echo (which is stdout) to what we stored in file descriptor 123:

#!/bin/bash

trap_function(){
    trap '' EXIT INT TERM
    echo "This is redirected to 'output'." >&123
    touch this_will_exist
}
trap trap_function EXIT INT TERM

eval "sleep 100" 123>&1 &> output
katosh
  • 372
  • 4
  • 12