2

How do I make a sourced script auto-return if any command inside it fails?

Suppose I have a script that auto-exits on failure via set -e, e.g.

#!/bin/bash
# foo.env
set -e        # auto-exit on any command failure
echo "hi"
grep 123 456  # this command will fail (I don't have a file named "456")
echo "should not reach here"

If I run the command normally, it will auto-exit on the failed grep command:

box1% ./foo.env
hi
grep: 456: No such file or directory

However, if I source the script, it exits my current shell, not just the script being sourced:

box1% ssh box2
box2% source ./foo.env
hi
grep: 456: No such file or directory
Connection to box2 closed.
box1%

If I remove the set -e,

#!/bin/bash
# foo2.env
echo "hi"
grep 123 456  # this command will fail (I don't have a file named "456")
echo "should not reach here"

then it doesn't auto-exit or auto-return the sourced script at all:

box1% ssh box2
box2% source ./foo2.env
hi
grep: 456: No such file or directory
should not reach here
box2%

The only workaround I've found so far is to add a return expression to every line of code in the script, e.g.

box1% cat foo3.env
#!/bin/bash
# foo3.env - works, but is cumbersome
echo "hi" || return
grep 123 456 || return
echo "should not reach here" || return

box1% source foo3.env
hi
grep: 456: No such file or directory
box1%

Is there another way for sourced scripts, similar to how set -e works for non-sourced ones?

1

When you source a script is like if you were writing that file line by line from the keyboard. It means that set -e will regard the current shell and that on error it will exit from the current shell.

That's a workaround. Today I feel me lazy so I thought that computer can write ||return for me, or better to read line by line the file and execute so:

#!/bin/bash
# this is the file MySource.sh
while IFS='' read -r line
do
   [[ $line == \#* ]] || $line || return
done < "$1"

execute it with . MySource.sh FileToBeSourced.sh

If your FileToBeSourced.sh script is with one line commands it should work.
Far far away to be ready to be used in a production environment.
Test it and eventually use it at your own risk.

It skips lines that start with # because they should cause the error # command not found.

| improve this answer | |
  • That's a clever trick. It turns out my scripts span many lines, so I'd have to make a fancier parser. Maybe the best way is to find a parser or hack one up in Perl and have it stream commands instead of read. – Mr Fooz Jun 9 '15 at 18:14
  • @MrFooz: Thanks, it was just an idea. With a fancy parser you can get rid of the ungraceful [[ $line == \#* ]] || too. Maybe you can put the parsed output at the end of the loop do.. done <<< $( FancyParser.sh "$1" ) – Hastur Jun 10 '15 at 8:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.