4

So this should be simple. I am trying to test a condition at the top of a script and bail out of the entire script when the condition fails, and I have two statements I want to execute when I bail. With a lone exit and no second statement, it's fine, but no matter how I add a second statement, I can't get it to completely exit. The implicit cosmetic rule is that this must be all on one line. I discovered this when this one-liner change to a script did not work as intended. The script kept going.

First let me show what does work. The following line completely exits if the $USER variable isn't 'x', and that's good. I know this works because typing this line into a terminal window will close that terminal window (unless your user id really is 'x'), so it's really doing a top-level exit:

[ $USER = 'x' ] || exit 1

So this is good and just as I want, except I want to echo a message before exiting; however, if I try to add the echo, the exit no longer occurs or rather it seems to occur "differently," like perhaps in a bash function context or something. The next line will not close your terminal, and this is bad.

[ $USER = 'x' ] || (echo "Time to bail" ; exit 1)

I thought maybe the semi-colon was getting eaten by echo, but no, the exit does seem to be getting hit. How do I know? I changed the code above and then echoed $? and I saw whatever value I put where you see "1" above. And of course I was viewing these values in a terminal window that I wanted to be closed, and it wasn't closed.

The next variation also shows a second way to perform an echo and a second statement, but again the exact same behavior occurs when an exit is used:

[ $USER = 'x' ] || (echo "Time to bail" && exit 1)

I'm hoping someone here is going to make this all not only not strange but sensible-seeming.

So is this not possible to do on one line?

(bash --version: GNU bash, version 4.3.30(1)-release)

  • 2
    ! true || { echo false; exit 1; } – mikeserv Oct 21 '15 at 5:56
  • @mikeserv, OK, I think that's working. Why does it need curly braces? – user62177541 Oct 21 '15 at 6:02
  • I see it now, I just found this. The parens are creating a sub-shell. – user62177541 Oct 21 '15 at 6:08
5

What you're searching is something like this:

[ "$USER" = "x" ] || { echo "Time to bail"; exit 1; }

The { list; } statement executes the commands in the given list in the current shell context. No subshell is created, unlike in the ( list ) notation. An exit call between parentheses will exit that subshell and not the parent shell itself.


The examples in your question with the if-statement on one line or multiple lines are syntactically correct. I cannot reproduce that behavior. It doesn't matter how many lines there arre; the if-statement never starts a subshell in its body.


BTW: I added double quotes to the variable in the condition, because when the variable $USER is empty, the construct would expand to [ = "x" ] which is not valid. With double quotes it expands to [ "" = "x" ].

  • It's my fault... I have a mistake up there in the if. It should be a not condition. Oops. The parens was the main question though.... – user62177541 Oct 21 '15 at 6:25
  • @user62177541 Are you sure that the condition is true? I ask this because the main problem was about condition || command -> if condition is not true then command and the second problem is if [ condition ]; then command; fi -> if the condition is true then ... – chaos Oct 21 '15 at 6:29
  • The curly braces seem to require a lot of semi-colons. Annoying. Also, the && form is now illegal syntax with the curly braces. – user62177541 Oct 21 '15 at 6:48
  • @user62177541 You only needed semicolon is the one behind the last command (but it can also be a newline). – chaos Oct 21 '15 at 6:50
  • OK, I guess this is another version having only one semi-colon: [ $USER == 'x' ] || { echo Time to bail && exit 1 ; } – user62177541 Oct 21 '15 at 6:53
0

Thanks to @mikeserv's hint, the answer is extraordinarily simple and it has to do with the importance of recognizing that parentheses present a very special kind of grouping. Parentheses create a sub-shell, and that is exactly why I was seeing the lack of what I called a "top-level" exit.

A complete explanation is here, and here's the key excerpt:

Placing a list of commands between parentheses causes a subshell environment to be created... Placing a list of commands between curly braces causes the list to be executed in the current shell context.

What is a bit unpleasant here though is that the two are not completely interchangeable and the curly braces require a semi-colon before the final curly while the parentheses do not. The syntax I am settling on tries to eliminate the semi-colons and use the "&&" form:

[ $USER == 'x' ] || { echo "Time to bail" && exit 1 ; }

So all in all, the parentheses are nicer to look at than the curlies, and the curlies with their semi-colon requirement make the one liner look less like a one-liner.

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.