6

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)

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

2 Answers 2

9

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" ].

9
  • 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.... Commented Oct 21, 2015 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
    Commented Oct 21, 2015 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. Commented Oct 21, 2015 at 6:48
  • @user62177541 You only needed semicolon is the one behind the last command (but it can also be a newline).
    – chaos
    Commented Oct 21, 2015 at 6:50
  • OK, I guess this is another version having only one semi-colon: [ $USER == 'x' ] || { echo Time to bail && exit 1 ; } Commented Oct 21, 2015 at 6:53
1

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .