25

I ran an executable in bash

./code > log

It shows occasional error messages on terminal whereas all printf statements go into log file. I re-run it like below

./code >& log

Now, the occasional error messages also go into log. But if there is a segmentation fault, it is still shown on terminal. Why? How to make the message Segmentation fault (core dumped) go into the log file?


user$ bash --version

GNU bash, version 4.2.24(1)-release (i686-pc-linux-gnu)

3 Answers 3

27

The “segmentation fault” message is printed to stderr, but it's the shell's standard error, not the program's standard error. The shell prints this message when it detects that the program has terminated due to a signal.

You can silence the message by redirecting stderr around the part of the shell script that runs the program:

{ ./code; } >&log
3
  • Unless I'm missing something, this silences the error, but doesn't capture the Segmentation Fault message. Nov 6, 2022 at 21:03
  • @BenAveling This puts the “Segmentation fault” message in the file log, which is what the question asked for. What else are you expecting? Nov 6, 2022 at 22:05
  • PEBKAC at my end - instead of piping to a file, I was trying to capture the Seg fault in a variable, which doesn't work. Nov 6, 2022 at 22:21
17

A segmentation fault is a signal, if you are not catching this then your program will be terminated and your shell will print this to its stderr (rather than your program's stderr).

It is possible for either your program or the shell to take specific actions when this occurs, either by the program catching the signal or your shell trapping the SIGCHILD signal and then checking your child's exit status.

9
  • 1
    @user13107 help trap Oct 30, 2012 at 8:31
  • 2
    yup. got it. if anyone's interested, here's what i did pastebin.com/QyeJYYHC
    – user13107
    Oct 30, 2012 at 9:50
  • 1
    The shell trap command traps signals sent to the shell. So it won't work to catch the one being sent to your program.
    – derobert
    Oct 31, 2012 at 11:36
  • 1
    @warl0ck it is possible to catch a segfault in the same way you catch any signal, however this can lead to undefined behavior, but if you know what you are doing you may be able to at least die in a sensible way. The OP wanted to print to stderr, in this case catching the segfault and printing is safe.
    – cjh
    Oct 31, 2012 at 18:43
  • 1
    @warl0ck: you can, it's just a very bad idea to do anything in the handler but log and exit. There are some specialized use cases though.
    – Linuxios
    Nov 5, 2012 at 22:38
0

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .