Reputation: 76909
In a shell script to be executed, I can abort on errors using set -e
.
In a sourced script, however, using set -e
will kill the original shell if a later command exits with an error status.
source set_e.sh
./exit_1.sh
# shell dies
A trivial solution would be to set +e
at the end of the script, but this would break the parent's set -e
if used (which may very well happen if someone wraps my script in the future).
How can I get abort-on-error functionality in a sourced script?
Upvotes: 20
Views: 10352
Reputation: 51
Setting, catching and unsetting set -e
turned out rather fragile for me.
Thus I prefer trapping for errors.
This is a one-liner I often use, including trying a silenced return
(works if sourced), otherwise exit
. Also, unsetting the trap, since otherwise that one stays around from sourced scripts.
trap 'echo Error $? on line $LINENO; trap - ERR; return 2>/dev/null || exit' ERR
Upvotes: 2
Reputation: 123450
You can check if set -e
is already enabled and conditionally set it afterwards:
[[ $- == *e* ]] && state=-e || state=+e
set -e
yourcode
set "$state"
Note, however, that set -e
is buggy and scripts should never depend on it for correctness. For example, if someone sources your script in an if
statement, set -e
may no longer work correctly:
echo '
set -e
ls file.that.doesnt.exist
echo "Success"
' > yourscript
set -e
if ! source yourscript
then
echo "Initialization failed"
fi
echo "Done"
then set -e
will longer abort on failure in yourscript
in bash 4.3.30:
ls: cannot access file.that.doesnt.exist: No such file or directory
Success
Done
while it will exit the entire script in bash 2, 3 and up to 4.2:
ls: cannot access file.that.doesnt.exist: No such file or directory
Upvotes: 2
Reputation: 424
Well, the question is not very clear re: what the original author wanted after intercepting the error in the sourced script, however, as an entry point for the solution the following will suffice:
You can set a trap on ERR and handle the error inside the sourced script there. Below are two scenarios: one with the sourced script using "set -e" and another with the sourced script NOT using "set -e".
The primary script is calling the secondary script with defined "set -e" and catches an error:
[galaxy => ~]$ cat primary.sh
#!/bin/sh
set -e
echo 'Primary script'
trap 'echo "Got an error from the secondary script"' ERR
source secondary.sh
trap - ERR
echo 'Primary script exiting'
[galaxy => ~]$ cat secondary.sh
#!/bin/sh
echo 'Secondary script'
set -e
echo 'Secondary script generating an error'
false
echo 'Secondary script - should not be reached'
[galaxy => ~]$ ./primary.sh
Primary script
Secondary script
Secondary script generating an error
Got an error from the secondary script
[galaxy => ~]$
The primary script is calling the secondary script without "set -e" and catches an error:
[galaxy => ~]$ cat primary.sh
#!/bin/sh
set -e
echo 'Primary script'
trap 'echo "Got an error from the secondary script"' ERR
source secondary.sh
trap - ERR
echo 'Primary script exiting'
[galaxy => ~]$ cat secondary.sh
#!/bin/sh
echo 'Secondary script'
echo 'Secondary script generating an error'
false
echo 'Secondary script - should not be reached if sourced by primary.sh'
[galaxy => ~]$ ./primary.sh
Primary script
Secondary script
Secondary script generating an error
Got an error from the secondary script
[galaxy => ~]$
As a bonus: intercepting an error in the sourced script and continuing:
[galaxy => ~]$ cat primary.sh
#!/bin/sh
echo 'Primary script'
i=0
while [ $i = 0 ]; do
i=1
trap 'echo "Got an error from the secondary script"; break' ERR
source secondary.sh
done
trap - ERR
echo 'Primary script exiting'
[galaxy => ~]$ cat secondary.sh
#!/bin/sh
echo 'Secondary script'
echo 'Secondary script generating an error'
false
echo 'Secondary script - should not be reached if sourced by primary.sh'
[galaxy => ~]$ ./primary.sh
Primary script
Secondary script
Secondary script generating an error
Got an error from the secondary script
Primary script exiting
[galaxy => ~]$
Upvotes: 5
Reputation: 84551
Change set -e
to return 0
(or choose your favorite integer instead of 0
). You can think of it as treating your sourced file as a function. For example:
$ cat myreturn.sh
#!/bin/bash
let i=0
while test "$i" -lt 10; do
echo "i $i"
if test "$i" -gt 5 ; then
return 5
fi
sleep 1
((i+=1))
done
return 4
$ ( . myreturn.sh )
i 0
i 1
i 2
i 3
i 4
i 5
i 6
$ echo $?
5
Upvotes: 4
Reputation: 75478
It's impossible. However you can opt to use a subshell if you want:
(
set -e
source another.sh
)
Only that environment of calling script can never be altered by the called script.
Note: It may be important to separate both commands with newline and not use a semicolon.
Upvotes: 9
Reputation: 2053
You could detect by set -o
if the errexit option was set at the beginning of the sourced script and recover its original value at the end of the sourced script.
Upvotes: 0