4

Is there a generic way in a bash script to "try" something but continue if it fails? The analogue in other languages would be wrapping it in a try/catch and ignoring the exception.

Specifically I am trying to source an optional satellite script file:

. $OPTIONAL_PATH

But when executing this, if $OPTIONAL_PATH doesn't exist, the whole script screeches to a halt.

I realize I could check to see if the file exists before sourcing it, but I'm curious if there is a generic reusable mechanism I can use that will ignore the error without halting.

Update: Apparently this is not normal behavior. I'm not sure why this is happening. I'm not explicitly calling set -e anywhere ($- is hB), yet it halts on the error. Here is the output I see:

./script.sh: line 36: projects/mobile.sh: No such file or directory

I added an echo "test" immediately after the source line, but it never prints, so it's not anything after that line that is exiting. I am running Mac OS 10.9.

Update 2: Nevermind, it was indeed shebanged as #!/bin/sh instead of #!/bin/bash. Thanks for the informative answer, Kaz.

1
  • If you allow the $OPTIONAL_PATH to be non-existent, just check if it exists and source it only when it's available. Commented Nov 20, 2013 at 19:27

3 Answers 3

5

Failed commands do not abort the script unless you explicitly configure that mode with set -e.

With regard to Bash's dot command, things are tricky. If we invoke bash as /bin/sh then it bails the script if the . command does not find the file. If we invoke bash as /bin/bash then it doesn't fail!

$ cat source.sh 
#!/bin/sh

. nonexistent
echo here
$ ./source.sh 
./source.sh: 3: .: nonexistent: not found
$ ed source.sh 
35
1s/sh/bash/
wq
37
$ ./source.sh 
./source.sh: line 3: nonexistent: No such file or directory
here

It does respond to set -e; if we have #!/bin/bash, and use set -e, then the echo is not reached. So one solution is to invoke bash this way.

If you want to keep the script maximally portable, it looks like you have to do the test.

The behavior of the dot command aborting the script is required by POSIX. Search for the "dot" keyword here. Quote:

If no readable file is found, a non-interactive shell shall abort; an interactive shell shall write a diagnostic message to standard error, but this condition shall not be considered a syntax error.

Arguably, this is the right thing to do, because dot is used for including pieces of the script. How can the script continue when a whole chunk of it has not been found?

Otherwise arguably, this is braindamaged behavior inconsistent with the treatment of other commands, and so Bash makes it consistent in its non-POSIX-conforming mode. If programmers want a command to fail, they can use set -e.

I tend to agree with Bash. The POSIX behavior is actually more broken than initially meets the eye, because this also doesn't work the way you want:

if . nonexistent ; then
  echo loaded
fi

Even if the command is tested, it still aborts the script when it bails.

Thank GNU-deness we have alternative utilities, with source code.

Sign up to request clarification or add additional context in comments.

3 Comments

I haven't intentionally called set -e anywhere. Is there a way I can check if that has been called elsewhere?
Check the value of $- for the letter e.
Aha, it was shebanged as #!/bin/sh! There's the answer.
3

You have several options:

  1. Make sure set -e wasn't used, or turn it off with set +e. Your bash script should not exit by default simply because the . command failed.

  2. Test that the file exists prior to sourcing.

    [ -f "$OPTIONAL_PATH" ] && . "$OPTIONAL_PATH"
    

    This option is complicated by the fact that if $OPTIONAL_PATH does not contain any slashes, . will still try to find the file in your path.

  3. If you want to keep set -e on, "hide" the failure like this:

    . "$OPTIONAL_PATH" || true
    

    Even if the source fails, the exit status of the command list as a whole will be 0, due to the || true.

(Much of this is covered [better] by Kaz's answer, especially the references to the POSIX standard, but I wasn't sure when or if he would undelete his answer.)

2 Comments

I temporarily deleted the answer because it contained an incorrect statement about the dot command. (It is the dot command which is standard and source which is nonstandard; I mixed them up).
That's the part I noticed :) Good answer; I was unaware that POSIX required a non-interactive shell to abort. bash doesn't even do it when using the --posix option.
1

This is not the default behavior. Did you set -e or use #!/bin/bash -e anywhere in your script, to make it automatically exit on failure?

If so, you can use

. $OPTIONAL_PATH || true

to continue anyways.

1 Comment

Even adding || true continues to abort.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.