What are these scope changes in a bash script doing?

Viewed 52

If you have something like this:

#!/bin/bash
(
  x=foo
  { echo $x }
)

What is the significance of the parentheses and braces? What are these constructs known as? What properties do they have? What are they used for etc.?

2 Answers

The commands enclosed in round parentheses (...) are run in a subshell. They inherit the environment from the parent shell, but any changes they make isn't propagated back to the parent.

{ echo $x } is wrong, there's a missing ; before the closing }. Commands run in { ... } are run in the context of the current shell, it's usually used when you need to redirect output of several commands, e.g.

{
    echo 1
    echo 2
} > log

Note that the final ; is not needed if there's a newline in its place.

re: the (curly) braces ({...}) ... from the man pages for bash:

   { list; }
          list is simply executed in the current shell environment.  list  must  be  termi‐
          nated with a newline or semicolon.  This is known as a group command.  The return
          status is the exit status of list.  Note that unlike the metacharacters ( and  ),
          {  and  } are reserved words and must occur where a reserved word is permitted to
          be recognized.  Since they do not cause a word break, they must be separated from
          list by whitespace or another shell metacharacter.

The provided example of a single command inside braces ({ echo $x }), setting aside the syntax issue, doesn't make much sense (ie, it's not any different than echo $x).

One example from linux.com:

$ { echo "I found all these PNGs:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls; } > PNGs.txt

# or

$ { echo "I found all these PNGs:"
find . -iname "*.png"
echo "Within this bunch of files:"
ls
} > PNGs.txt

Here the {...} groups all of the output together so that only a single > PNGs.txt is required to send the output from all 4x commands to the file PNGs.txt.

Without the {...} you would need:

$ echo "I found all these PNGs:"     >  PNGs.txt
$ find . -iname "*.png"              >> PNGs.txt
$ echo "Within this bunch of files:" >> PNGs.txt
$ ls                                 >> PNGs.txt

For the following set of commands the (...) and {...} generate the same result (all output sent to file PNGs.txt) ...

{ echo "I found all these PNGs:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls; } > PNGs.txt

( echo "I found all these PNGs:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls; ) > PNGs.txt

... with the 2nd option incurring the additional overhead of spawning a subshell.

With the following example we see the different effects of execution in the current/parent shell vs execution in the subshell:

$ { x=5 ; }           # defined in current/parent shell
$ ( x=7 ; )           # defined in subshell, not visible to parent; `;` is optional
$ echo $x
5
Related