Issue
I want to add an error trap function to my bash script. I cannot figure out how to call helper functions (which may themselves contain errors) from the error trap function without very weird behavior.
Running the following code snippet
#!/bin/bash
# enable bash strict mode
# -u: treat unset variables as an error
# -o pipefail: set the exit status of a pipeline to the exit status of the rightmost non-zero exit status
# -e: Script exits immediately on errors
set -e -u -o pipefail
on_error() {
echo "Start error"
local helper_exit_status=0
helper || helper_exit_status=$?
echo "Finish error $helper_exit_status"
}
helper() {
echo "Start helper"
ls /nonexistent-directory-2
echo "Finish helper" # <-- I would expect this to not be executed due to "set -e"
}
trap 'on_error' ERR
echo "Start main"
ls /nonexistent-directory-1
echo "Finish main"
with bash yields
$ bash ./test.sh
Start main
ls: cannot access '/nonexistent-directory-1': No such file or directory
Start error
Start helper
ls: cannot access '/nonexistent-directory-2': No such file or directory
Finish helper
Finish error 0
I think what is happening is this:
- Print "Start main"
ls /nonexistent-directory-1
has non-zero exit status- This triggers a call to
on_error
- Print "Start error"
- Call
helper
, where the "||" means that subsequent non-zero exit status is ignored - Print "Start helper"
ls /nonexistent-directory-1
has non-zero exit status (ignored)- Print "Finish helper"
- The last command was
echo
, which had non-zero exit status, sohelper_exit_status
remains 0
How can I fix this such that:
- My script still calls
on_error
when encountering errors on_error
can call helper functions likehelper
- An error in
helper
immediately abortshelper
(this is the expected behavior due toset -e
, and I should not need to modifyhelper
to achieve this as it can also be called from other locations) - After
helper
encounters an error,on_error
continues its execution but learns about the error due to a non-zerohelper_exit_status
My bash version:
$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Solution
set -e and Functions: When set -e is active, Bash will exit immediately if a command exits with a non-zero status, unless the non-zero status is part of an if statement, part of a test within a list construct (&& or ||), or the command's return status is being inverted with !.
Helper Function: When the helper function is called with ||, the non-zero status of ls is considered to be controlled by the ||. Therefore, the script doesn't exit, and the echo "Finish helper" command is executed.
Error Status in on_error Function: The command helper || helper_exit_status=$? sets helper_exit_status to 0 because the last command (echo) in the helper function exits successfully.
Here's an updated version of your script:
#!/bin/bash
set -e -u -o pipefail
on_error() {
echo "Start error"
local helper_exit_status=0
helper
helper_exit_status=$?
echo "Finish error $helper_exit_status"
}
helper() {
echo "Start helper"
ls /nonexistent-directory-2 || return $?
echo "Finish helper"
}
trap 'on_error' ERR
echo "Start main"
ls /nonexistent-directory-1
echo "Finish main"
Answered By - Przemysław Doczkal Answer Checked By - Senaida (WPSolving Volunteer)