This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the bash category.
Last Updated: 2024-11-23
I came across something that surprised me when writing a shell script that was supposed to exit early. Look at this code:
fail() {
echo "Fail"
exit 1
}
main() {
cat Gemfile | while read -r line; do
echo "$line"
fail
done
echo "I should not be executed"
exit 0
}
When I ran this, the echoing of the output from the Gemfile
stopped after the
first line (as expected) but I also saw the output 'I should not be executed.',
which was not expected.
By comparison, this issue did not happen here:
main() {
fail
echo "I should not be executed"
exit 0
}
main
The pipe operator spawned the while loop in a subshell and the exit 1
in the
fail
function only affects this subshell. So the looping stops, but not the
parent shell.
The easiest way is to avoid a subshell by using redirection instead:
This behaves as expected
while read -r line < Gemfile; do
echo "$line"
fail
done
i.e. How would you rewrite the following?
git diff --cached --name-only | while read -r line; do
...
done
We would get the output using command substitution:
changed_files() {
git diff --cached --name-only
}
main() {
for file in $(changed_files); do
echo "$file"
check_for_debuggers "$file"
done
echo "I should not be executed"
exit 0
}