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
$IFSfor a few commands, but I don't want to alter$IFSglobally for the current shell cdsomewhere, but I don't want to change the$PWDfor 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[@]}" 11unusual 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
- 89,123
- 21
- 245
- 323
- 17,625
- 2
- 37
- 60
-
16After 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
-
6*Hint:* 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
From the official bash documentation:
()
( list )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.
{}
{ list; }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.
- 2,415
- 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
- 306
- 1
- 5
(...) are used to run code in a sub-shell. Code used bewteen {...} won't be used in a sub-shell.
- 89,123
- 21
- 245
- 323
- 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
localcan 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
- 331
- 1
- 6