77

I have a small snippet of a shell script which has the potential to throw many errors. I have the script currently set to globally stop on all errors. However i would like for this small sub-section is slightly different.

Here is the snippet:

recover database using backup controlfile until cancel || true; 
auto

I'm expecting this to eventually throw a "file not found" error. However i would like to continue executing on this error. For any other error i would like the script to stop.

What would be the best method of achieving this?

Bash Version 3.00.16

codeforester
  • 28,846
  • 11
  • 78
  • 104
Stunt
  • 1,044
  • 1
  • 8
  • 18
  • 2
    Does [this](http://stackoverflow.com/questions/64786/error-handling-in-bash) help? And, Welcome to SO! :) – S.R.I Jul 24 '13 at 09:42
  • That does help to improve my error reporting however it does not include anything for handling different errors in a different manor. I'm essentially trying to mimic the try{}catch{} from c# – Stunt Jul 24 '13 at 10:00

2 Answers2

147

In order to prevent bash to ignore error for specific commands you can say:

some-arbitrary-command || true

This would make the script continue. For example, if you have the following script:

$ cat foo
set -e
echo 1
some-arbitrary-command || true
echo 2

Executing it would return:

$ bash foo
1
z: line 3: some-arbitrary-command: command not found
2

In the absence of || true in the command line, it'd have produced:

$ bash foo
1
z: line 3: some-arbitrary-command: command not found

Quote from the manual:

The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted with !. A trap on ERR, if set, is executed before the shell exits.

EDIT: In order to change the behaviour such that in the execution should continue only if executing some-arbitrary-command returned file not found as part of the error, you can say:

[[ $(some-arbitrary-command 2>&1) =~ "file not found" ]]

As an example, execute the following (no file named MissingFile.txt exists):

$ cat foo 
#!/bin/bash
set -u
set -e
foo() {
  rm MissingFile.txt
}
echo 1
[[ $(foo 2>&1) =~ "No such file" ]]
echo 2
$(foo)
echo 3

This produces the following output:

$ bash foo 
1
2
rm: cannot remove `MissingFile.txt': No such file or directory

Note that echo 2 was executed but echo 3 wasn't.

devnull
  • 103,635
  • 29
  • 207
  • 208
  • Is this more of a replacement for my "set +e and set -e" usage? I'm not sure i understand how this handles varying errors. – Stunt Jul 24 '13 at 10:04
  • @Stunt This isn't a replacement. It just implies that if you had `set -e` at the beginning of the script, you don't need to say `set +e` to ignore errors. Saying `command || true` would suffice. – devnull Jul 24 '13 at 10:07
  • That's helpful for cleaning up my code (The set +e and set -e can get quite messy!) However it's still handling errors on a more general basis. For this question i'd like to inspect the error and then continue or exit based on which error is thrown. (Can't see how to vote up your comment as helpful!) – Stunt Jul 24 '13 at 10:15
  • That looks like it could be exactly what i need! However, on testing it's syntax errors. Here's my test: #!/bin/bash set -u set -e foo() { rm MissingFile.txt } echo 1 [[ $(foo 2>&1) =~ "No such file" ]] echo 2 This produces a '(' expected error. (I've used a function here as i suspect this will be required for my example). – Stunt Jul 24 '13 at 12:42
  • Sorry my error i reported should say '(' UNexpected. It seems to be complaining about the ( before "foo" – Stunt Jul 24 '13 at 12:52
  • Not sure what's causing the error for you. See the edit above for your test. – devnull Jul 24 '13 at 13:00
  • The edit looks great, however if you change the text from "File not found" to "bbb" (Which should never return"). You will receive "2" on the output. This suggests to me that the error checking may not be working correctly – Stunt Jul 24 '13 at 13:11
  • Didn't you say that you wanted to continue only if the error matched a pattern? The script stops because your error doesn't match the pattern `bbb` and returns with a non-zero exit code. – devnull Jul 24 '13 at 13:12
  • Sorry, i should have been clearer. My test showed that the test did not produce a non-zero exit code as it successfully outputted the next line "2". I would be looking for only "1" to be outputted in this scenario. – Stunt Jul 24 '13 at 13:16
  • Looks like you're using an older version of `bash`. This should work well with bash 4+. – devnull Jul 24 '13 at 13:29
  • Version 3.00.16 installed at the moment – Stunt Jul 24 '13 at 13:34
  • Apologies it took so long, i've only just managed to test this on 4.2. This solution has worked. Thanks! :) – Stunt Jul 25 '13 at 10:35
35

Use:

command || :

: is a bash built-in that always returns success. And, as discussed above, || short-circuits so the RHS is only executed if the LHS fails (returns non-zero).

The above suggestions to use 'true' will also work, but are inefficient as 'true' is an external program.

Chris Cogdon
  • 6,041
  • 5
  • 32
  • 29
  • 7
    True is only an external program if you run `/bin/true`, as `true` on its own is a shell built-in of bash, just like echo and test, which also exists as external programs but are also built-ins in bash and if you don't give a full path, the built-ins will be used. – Mecki Dec 21 '16 at 13:57