I'm studying shell scripting with bash and I need to know the difference between (...)
and {...}
. How does one select between the two when writing a script?
-
1See http://wiki.bash-hackers.org – 0x2b3bfa0 Apr 07 '15 at 14:34
-
3You meant in the context of command grouping only? – heemayl Apr 07 '15 at 14:35
5 Answers
If you want the side-effects of the command list to affect your current shell, use {...}
If you want to discard any side-effects, use (...)
For example, I might use a subshell if I:
- want to alter
$IFS
for a few commands, but I don't want to alter$IFS
globally for the current shell cd
somewhere, but I don't want to change the$PWD
for the current shell
It's worthwhile to note that parentheses can be used in a function definition:
normal usage: braces: function body executes in current shell; side-effects remain after function completes
$ count_tmp() { cd /tmp; files=(*); echo "${#files[@]}"; } $ pwd; count_tmp; pwd /home/jackman 11 /tmp $ echo "${#files[@]}" 11
unusual usage: parentheses: function body executes in a subshell; side-effects disappear when subshell exits
$ cd ; unset files $ count_tmp() (cd /tmp; files=(*); echo "${#files[@]}") $ pwd; count_tmp; pwd /home/jackman 11 /home/jackman $ echo "${#files[@]}" 0

- 90,397

- 17,900
-
17After many years of shell development I didn't know you could use parentheses to run functions in subshells. What a great idea to avoid polluting the global namespace! – l0b0 Apr 08 '15 at 07:33
-
10Using the
local
keyword goes a long way to cleaning up that pollution. – glenn jackman Apr 08 '15 at 10:54 -
2Yeah, but you have to remember to declare every variable local, and it clutters the code. – l0b0 Apr 08 '15 at 15:00
-
6Hint: If you want side-effect-free functions but avoid the unusual function declaration syntax (which code editors may not be aware of) then just use parentheses on the function call rather than the declaration:
pwd; (count_tmp); pwd;
– Juve Jul 08 '15 at 15:11 -
2to the shell... foo() (:;) is equivalent to foo() { (:;); } That is how it reports it if you ask! – anthony Nov 15 '16 at 06:01
-
It's also worth noting that '{}' imposes different syntactic requirements, e.g.
(true)
works, but with braces you need{ true; }
– Britton Kerin Feb 20 '24 at 19:19
From the official bash documentation:
()
( list )
<p>Placing a list of commands between parentheses causes a subshell environment to be created, and each of the commands in list to be executed in that subshell. Since the list is executed in a subshell, variable assignments do not remain in effect after the subshell completes.</p>
{}
{ list; }
<p>Placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created. The semicolon (or newline) following list is required.</p>

- 2,445
- 15
- 24
Code in '{}' is executed in the current thread/process/environment and changes are preserved, to put it more succinctly, the code is run in the current scope.
Code in '()' is run inside a separate, child process of bash that is discarded after execution. This child process is often referred to as a
sub-shell and can be thought of as a new, child-like scope.
As an example consider the following...
~ # { test_var=test ; }
~ # echo $test_var
test
~ # ( test_var2=test2 )
~ # echo $test_var2
~ #
Notice in the first example with '{}' the variable is still set even after the closing '}', whereas in the example with '()' the variable is not set outside the scope of the '()'.
Also note the '{}' and '()' syntax difference. The ";" delimiter is always required for code in '{}', but not for code in '()'.

- 103
- 2

- 316
(...)
are used to run code in a sub-shell. Code used bewteen {...}
won't be used in a sub-shell.

- 90,397

- 415
- 1
- 4
- 11
In addition to the other answers here, I'd like to expand on the main trade-offs between ()
and {}
:
- speed
- code readability
- thread pollution
Specific pros and cons with examples
The following 3 functions do almost exactly the same thing: they define a variable, and then clear it:
funct1(){ local myvar; myvar="boo"; }
- Pro: very fast
- Pro: does not overwrite global variables with the same name
- Con: bug risk: other changes in this function (like
cd mydirectory/
) remain after function has finished - Con: readability: requires variables be initiated with
local myvar myvar2 ...
- Con: bug risk: forgetting to initiate with
local
can lead to overwriting global variables
funct2(){ myvar="boo"; unset myvar; }
- Pro: very fast (about the same as funct1, possibly negligibly faster)
- Con: bug risk: always overwrites global variables using same name
- Con: bug risk: other changes in this function (like
cd mydirectory/
remain after function has finished) - Con: readability: requires variables be unset with
unset myvar myvar2 ...
- Con: bug risk: forgetting to unset the variables can lead to global variables with unintended values
funct3()(myvar="boo")
- Con: takes ~40 to 45 times as long to execute as funct1 or funct2
- Pro: does not overwrite global variables with the same name
- Pro: other changes in this function (like
cd mydirectory/
are cleared after function has finished) - Pro: no explicit variable cleanup commands required
- Pro: simple and less prone to bugs
Benchmark
You can use this script to compare those 3 functions. Each test runs each function 1000 times, and the test is run 5 times. The average time for each function is output afterward.
#!/bin/bash
funct1(){ local myvar; myvar="boo"; }
funct2(){ myvar="boo"; unset myvar; }
funct3()(myvar="boo")
typeset -A tests
tests=(
[funct1]=0
[funct2]=0
[funct3]=0
)
for n in {0..5}; do
for f in funct1 funct2 funct3; do
start=$(date +%s.%N)
for i in {1..1000}; do $f; done
dur=$(echo "$(date +%s.%N) - $start" | bc)
tests[$f]="$(echo "$(printf "%.6f" "$dur") + ${tests[$f]}"|bc)"
done
done
for i in $(printf "%s\n" "${!tests[@]}"|sort); do
echo "$i average: $(printf "%.6f" "$(echo "${tests[$i]} / 5"|bc -l)")"
done
exit
Example output from running that script:
funct1 average: 0.014117
funct2 average: 0.012473
funct3 average: 0.558457

- 341