0

Is there any difference between:

set -e;
./create-git-tag.sh || {
   echo 'Could not create git tag.'
   exit 1;
}

and

set -e;
if ! ./create-git-tag.sh; then 
    echo 'Could not create git tag.'
    exit 1;
fi

I am guessing that the if block does not create a subshell and therefore would exit before the if block was entered? They appear to function the same, are there any differences in behavior?

4

2 Answers 2

3

Curly brackets { } don't create subshells, round brackets do:

BASH(1)

(list)

list is executed in a subshell environment.

{ list; }

list is simply executed in the current shell environment.

So, neither the if statement nor the ... || { } form have the potential to create a subshell.

A subshell environment may be created if round brackets ( ) are used instead of curly brackets. The OR operator || would only allow this stage to be reached if the first command returned a non-zero status (ie. ./create-git-tag.sh failed).

If set -e is used, all forms exhibit the same behaviour (upon the test command failing the shell exits immediately) – but behind the scenes the round brackets will have created a subshell (which doesn't appear to be useful in this example).

If set -e wasn't used, creating a subshell with ( ) would mean that the subshell would exit, but the calling shell would continue – commands which follow would still be executed.

In all cases the echo-exit command chain (and associated subshell for round brackets) is only entered upon the test command failing. But the purpose of making conditional logic is that something might fail or it might not – this is where your wording in the question doesn't make sense:

I am guessing that the if block does not create a subshell and therefore would exit before the if block was entered?

The if block is "entered", it runs the test command to determine what to do next.

Personally I find the if statement clearer to read and prefer to use that in scripts. Operators && and || I might use in if's place when I'm using the shell "interactively".

1
  • happen to know if ./command.sh is equivalent to exec command.sh? My guess is they are different. Commented Feb 3, 2020 at 18:24
2

My understanding is that they're equivalent; in both cases the exit will terminate the instance of the shell running the script.

Neither version of what you have creates a subshell. To prove this, consider the following experiments:

$ cat version1
#!/bin/bash

set -e

x=7

ls /tmp/does/not/exist &> /dev/null || {
    x=42
}

echo $x


$ ./version1
42
$ cat version2
#!/bin/bash

set -e

x=7

if [ ! ls /tmp/does/not/exist &> /dev/null ]; then
    x=42
fi

echo $x

$ ./version2
42

If you change the { } in version1 to () (as in the following version), that will create a subshell. As a result, the modification of x within the ( ) does not affect x in the calling shell:

$ cat version3
#!/bin/bash

set -e

x=7

ls /tmp/does/not/exist 2> /dev/null || (
    x=42
)

echo $x

$ ./version3
7
1
  • happen to know if ./command.sh is equivalent to exec command.sh? My guess is they are different. Commented Feb 3, 2020 at 18:25

You must log in to answer this question.

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