Where did my segfault go? – Paul Mairo – The faster you fail, the faster you learn
Where did my segfault go?
The other day I was iterating on a small C program with entr:
ls hello.c | entr -s "gcc -o hello hello.c && ./hello"
hello happened to segfault, but entr showed me… nothing. No Segmentation<br>fault, no non-zero exit visible, just complete silence after the compile<br>output. Hmm, very weird. Let’s prefix with bash -c:
ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'"
Still nothing. What about moving the command into a script:
#!/bin/bash<br>gcc -o hello hello.c && ./hello
And point entr to it:
ls hello.c | entr -s ./run.sh
./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello
There’s my segmentation fault!
The reason(s)
Thanks to superuser (remember that site we<br>used to use pre LLMs?) for pointing me in the right direction. The question is<br>not exactly the same, but it still helped me. At first, I thought the problem<br>was with entr because I could reproduce both with bash and fish.
Who actually prints “Segmentation fault”? Obviously not the crashing<br>program. It can’t since it’s dead. The message is printed by the shell when it<br>reaps a child that died from SIGSEGV. No parent shell, no message, simple<br>enough.
But wait, why does even the explicit bash -c 'gcc && ./hello' version stay silent?
Turns out bash likes to exec the command if it can. When you run bash -c<br>"some_command" and some_command is effectively the only thing to do, bash<br>doesn’t bother forking a new process for<br>it.<br>It just execves into it, replacing itself. It’s a nice optimization, invisible<br>99% of the time.
In the script version, entr runs ./run.sh as a child, and the script’s<br>shebang starts a fresh bash to interpret the file. That bash runs gcc,<br>forks for ./hello, waits, sees it died from SIGSEGV, and dutifully prints<br>Segmentation fault before exiting.
The follow-up
But then it occurred to me, maybe you don’t need a wrapper script after all, what<br>happens if I have the crashing command running in a subshell?
$ ls hello.c | entr -s "gcc -o hello hello.c && (./hello)"<br>hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )
Yep, I don’t think I have ever been that satisfied to see a program segfaulting.<br>Here the parens force ./hello into a forked subshell, so bash can’t exec its<br>way out of being the parent. When the subshell dies from SIGSEGV, bash reaps<br>it and prints the message like normal.
I could have stopped there but I wanted a way to test my second hypothesis. What<br>if you just make sure the shell has something to do after the crashing command?
$ ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'"<br>bash: line 1: 109516 Segmentation fault (core dumped) ./hello
Victory!
Now I should probably get back to writing some C…
Written on July 11, 2026