error messages get redirected but if there is a segmentation fault, it is only shown on the terminal. Why?
TL/DR as per Giles: the segmentation fault message isn't coming from the program's stderr
or stdout
, so redirecting those has no effect. The segmentation fault message is generated by the shell that ran the seg faulting child.
What happens behind the scenes is that after the child dies, the shell calls wait()
(or waitpid()
) and uses the response from that to determine whether the child process died because it was signaled.
If so, the shell usually prints a message, but not always.
Note that the shell doesn't magically somehow trap
the signal that kills the child. Only the child can catch the signal, or be killed by the signal. The parent is sent a SIGCHLD when the child dies, but that's a different signal.
As well as printing an error message, or not, the shell sets $?
to indicate the exit status.
$ ./div_by_0
Floating point exception
$ echo $?
136
The same applies to div by zero and other signals. See man 3 wait
for full details.
One consequence of this is that you will only get a message if the child process is the last in a chain of processes.
e.g.
$ ./div_by_0
Floating point exception
$ echo | ./div_by_0
Floating point exception
$ ./div_by_0 | wc
0 0 0
Capturing the output from a command into file does not prevent the shell from printing a message, as per Giles.
$ { ./div_by_0 ; } > log
Floating point exception
$ { ./div_by_0 ; } >& log
$ cat log
Floating point exception
However, capturing the output from a command into a variable does prevent the shell from printing a message.
$ a=$(./div_by_0)
$ echo $a
$
Capturing the output, whether into a variable or a file, does not prevent $?
being set.
$ { ./div_by_0 ; } >& log
$ echo $?
136
$ a=`{ ./div_by_0 ; }`
$ echo $?
136
Depending on what you want to do with the error message, the above may already meet your needs, or you may want to check $?
after you call your program.
You can also use trap ERR
to catch the error, and then invoke behaviour to check if $?
matches SIGSEGV or SIGFPE or whatever.
e.g.
$ trap 'exit_code=$?; echo "exit code: $exit_code"; if [[ $exit_code -eq 139 ]]; then echo "segfault"; fi ; if [[ $exit_code -eq 136 ]] ; then echo "div by 0"; fi ;' ERR
$ ./div_by_0
Floating point exception
exit code: 136
div by 0
Note, if you trap
on SIGCHLD
, your trap code will be invoked before $?
is set, which is unlikely to be what you want. The faulting command will appear to have returned '$?' = 0, while the following command will appear to have returned a non-zero $?
e.g.
$ ./div_by_0
exit code: 0
Floating point exception
$ ./div_by_1
exit code: 136 <-- this is $? from div_by_0, not from div_by_1
div by 0