I read a great article on Lobste.rs today by Paul Mairo about a subtle interaction between entr, bash, and segfault messages. It's one of those things that seems obvious in hindsight but catches everyone at least once.
You're iterating on a C program with entr (a tool that runs commands when files change):
ls hello.c | entr -s "gcc -o hello hello.c && ./hello"
Your program segfaults. But entr shows you… nothing. No "Segmentation fault". No error code. Just silence.
You try wrapping it in bash -c. Still nothing. You move the commands into a script file — and suddenly the segfault message appears.
The key insight: "Segmentation fault" is printed by the shell, not by the crashing program. The program is dead — it can't print anything. The shell prints that message when it reaps a child process that died from SIGSEGV.
But here's the tricky part. When you run:
bash -c "gcc && ./hello"
bash detects that ./hello is the last thing it needs to do. So instead of forking a child process, it execs into the command directly, replacing itself. Now there's no parent shell to reap the child — so no message.
This optimization is invisible 99% of the time, but it bites you exactly when you need debugging output.
The article demonstrates three ways to work around this:
ls hello.c | entr -s "gcc -o hello hello.c && (./hello)"
The parentheses force ./hello into a forked subshell. Bash can't exec away because it still owns the subshell process.
ls hello.c | entr -s "gcc -o hello hello.c && ./hello; true"
Bash can't exec into ./hello because it still has true to run afterward. Simple and effective.
Put the commands in a run.sh file and run that. The script's bash can't exec away because the script has multiple lines of work to do.
The Lobste.rs discussion added another layer. Modern Linux distros have core dumps disabled by default. Even if you re-enable them with ulimit -c unlimited, systemd may squirrel them away somewhere unexpected.
Some tools mentioned in the comments:
coredumpctl — systemd's tool for listing and retrieving core dumpsgdb — re-run the crashing program inside the debuggerThis is a case study in how invisible optimizations have visible consequences. Bash's exec optimization is great for performance, but it silently removes the parent shell that would normally report child process signals.
The broader lesson: when debugging, be suspicious of anything that "should just work." If a segfault message doesn't appear, ask yourself: who prints this message, and are they even running?