3

I need to execute a command in a script with set -e set. This command is an exception to the general script flow, in that it could fail and that would not be critical: the script can continue. I want that command to not interrupt the script; instead I want to keep the exit code for later evaluation.

I have come up with the following script:

#!/bin/bash

set -e
false && res1=$? || res1=$?
true  && res2=$? || res2=$?
echo $res1
echo $res2

(My command would be in place of false or true)

Is this the right approach?

EDIT

Incidentally, this very similar construct does not work at all:

#!/bin/bash

set -e
false || res1=$? && res1=$?
true  || res2=$? && res2=$?
echo $res1
echo $res2

EDIT

Testing one of the suggestions:

#!/bin/bash

set -e
false || { res1=$?; true; }
true  || { res2=$?; true; }
echo $res1
echo $res2

This does not work. Result is:

1
(empty line)
11
  • @Jahid: no, because a failing command will interrupt script, which is what I want to avoid Commented Nov 11, 2016 at 11:56
  • I see, I didn't notice that... Commented Nov 11, 2016 at 11:57
  • @Jahid: Maybe false ; res1=$? would be better? Would that record the result of the previous command, without interrupting script? Commented Nov 11, 2016 at 11:57
  • It would interrupt the script Commented Nov 11, 2016 at 11:57
  • false || { res1=$?; true; } Does that work ? Commented Nov 11, 2016 at 11:58

3 Answers 3

6

Don't try to use && and || to form a ternary expression; it's too fragile. Just use an if statement.

if cmd; then res=$?; else res=$?; fi

Better(?) yet, you can put the assignment in the condition itself, reducing repetition at the cost of having a seemingly vacuous if statement:

if cmd; res=$?; then :; fi

That might be more clearly written with a single || though:

{ cmd; res=$?; } || true

(Notice I've used true and : interchangeably; : is guaranteed by POSIX to be a shell built-in, but true is probably more readable.)

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

3 Comments

There you are talking!
@Jahid Thanks; no idea why I typed $! originally.
This kind of thing sometimes happens :D. When I upvoted your answer, there were three $!, yet I didn't notice that. Even after three upvotes, no-one noticed... :D
1

Putting the && after || doesn't have the effect that you want. For example,

false || echo true && echo false

will print both true and false, i.e whatever after && will be executed whether the first command was successful or not. You can do it like this:

command && res1=$? || res1=$?

In this case, if command succeeds, the third one doesn't get executed, if fails, the second one doesn't get executed.

EDIT

As chepnar mentioned, the use of && and || isn't full-proof. The behavior isn't guaranteed.

1 Comment

Ok, so my original implementation is the right one. Thanks!
0

Rather than capturing same exit code twice why not just turn off -e, capture the exit status and turn on -e again like this:

#!/bin/bash
set -e

set +e          # turn off exit on error
false; res1=$?  # run command1 and capture exit status1
true; res2=$?   # run command2 and capture exit status2
set -e          # turn on exit on error

echo $res1
echo $res2

4 Comments

Thanks, but I want to avoid having set +e / set -e all around my code.
Hmm ok , but it will be cleaner than capturing same $? twice.
Solution by @chepner is very clean.
Yes, totally agree with that.

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.