789

I am confused by the usage of brackets, parentheses, curly braces in Bash, as well as the difference between their double or single forms. Is there a clear explanation?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Tim
  • 1
  • 141
  • 372
  • 590

8 Answers8

708

In Bash, test and [ are shell builtins.

The double bracket [[, which is a shell keyword, enables additional functionality. For example, you can use && and || instead of -a and -o and there's a regular expression matching operator =~.

Also, in a simple test, double square brackets seem to evaluate quite a lot quicker than single ones.

$ time for ((i=0; i<10000000; i++)); do [[ "$i" = 1000 ]]; done

real    0m24.548s
user    0m24.337s
sys 0m0.036s
$ time for ((i=0; i<10000000; i++)); do [ "$i" = 1000 ]; done

real    0m33.478s
user    0m33.478s
sys 0m0.000s

The braces, in addition to delimiting a variable name are used for parameter expansion so you can do things like:

  • Truncate the contents of a variable

      $ var="abcde"; echo ${var%d*}
      abc
    
  • Make substitutions similar to sed

      $ var="abcde"; echo ${var/de/12}
      abc12
    
  • Use a default value

      $ default="hello"; unset var; echo ${var:-$default}
      hello
    
  • and several more

Also, brace expansions create lists of strings which are typically iterated over in loops:

$ echo f{oo,ee,a}d
food feed fad

$ mv error.log{,.OLD}
(error.log is renamed to error.log.OLD because the brace expression
expands to "mv error.log error.log.OLD")

$ for num in {000..2}; do echo "$num"; done
000
001
002

$ echo {00..8..2}
00 02 04 06 08

$ echo {D..T..4}
D H L P T

Note that the leading zero and increment features weren't available before Bash 4.

Thanks to gboffi for reminding me about brace expansions.

Double parentheses are used for arithmetic operations:

((a++))

((meaning = 42))

for ((i=0; i<10; i++))

echo $((a + b + (14 * c)))

and they enable you to omit the dollar signs on integer and array variables and include spaces around operators for readability.

Bare double parentheses ((...)) return 0 (true) if the value of the enclosed expression is non-zero, 1 (false) otherwise, so:

$ ((2 + 3 - 5)) || echo "last command returned 1"
last command returned 1
$ ((2 + 3 - 7)) && echo "last command returned 0"
last command returned 0

Single brackets are also used for array indices:

array[4]="hello"

element=${array[index]}

Curly brace are required for (most/all?) array references on the right hand side.

ephemient's comment reminded me that parentheses are also used for subshells. And that they are used to create arrays.

array=(1 2 3)
echo ${array[1]}
2
Mark Stewart
  • 2,046
  • 4
  • 22
  • 32
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 15
    WARNING: That function is a fork bomb, do not run it. See: http://en.wikipedia.org/wiki/Fork_bomb – Dennis Williamson Feb 03 '10 at 19:37
  • 5
    It's only a fork bomb if you invoke it with an additional `:`. – ephemient Feb 03 '10 at 19:42
  • 8
    Also for completeness, I just came across this in an old script: `$[expression]` ; this is the old, deprecated arithmetic expression syntax for the newer, preferred syntax: `$((expression))` – michael Oct 06 '12 at 07:53
  • 4
    @DennisWilliamson Another use of curly braces in `bash` is creating sequences, as peripherally mentioned below (http://stackoverflow.com/a/8552128/2749397) As I would like to comment a bit this feature (as you didn't mention it ;-) I'm taking the liberty of using the most voted answer as a vehicle... Two examples of sequence literals: `echo {01..12}` -> `01 02 03 04 05 06 07 08 09 10 11 12` (note the initial zero); `echo {C..Q}` -> `C D E F G H I J K L M N O P Q`. Its main use is in loops, e.g., `for cnt in {01..12} ; do ... ${cnt} ... ; done` – gboffi Oct 24 '14 at 14:41
  • 2
    @gboffi: The zero padding feature became available in Bash 4. Also, in Bash 4, you can specify an increment in a sequence: `echo {01..12..2}` -> "01 03 05 07 09 11". Thanks for the reminder about sequences. I'll add it to my answer. – Dennis Williamson Oct 24 '14 at 14:58
  • @DennisWilliamson Bash 4, yes, definitely yes! as a remark on the quantity of stuff they put in `bash` I didn't know about the increment parameter until your remark and I discovered the "initial 0" trick just by chance, as I tried it out of boredom and it worked... ah, the Bash shell... – gboffi Oct 24 '14 at 15:40
  • For folks new to bash, here's a more elaborate list of operations you can do with parameter expansion: http://wiki.bash-hackers.org/syntax/pe – wondersz1 Dec 07 '17 at 08:53
  • No surprise that it's faster when the shell interprets a built-in "test" (double brackets) symbol versus invoking an external "test" program (single bracket). Then again, the external shell command ("test" or "[" alias) doesn't depend on any particular shell so it's more portable. – Rick O'Shea Dec 26 '19 at 17:20
  • "Note that the leading zero and increment features weren't available before Bash 4." -- shame on you macos. bash3 is 13+years old as of this comment; bash4 is 11+ years old. But still, macOS ships with bash3 (june 2020) – JDS Jun 29 '20 at 22:14
  • 2
    What is the difference between `(( ... ))` and `$(( ... ))` ? What effect does adding the leading `$` have? – Simon Elms Nov 05 '21 at 23:46
  • 1
    @SimonTewsi: `$(())` is referred to as Arithmetic Expansion while `(())` is referred to as Arithmetic Evaluation. The former "allows the evaluation of an arithmetic expression and the substitution of the result" (example: `echo "Value: $((1 + 2))"` outputs `Value: 3`) and the latter only does the evaluation, not the substitution (example: `((a = 1 + 2))` sets the value of `a` to `3` but doesn't output anything. But then you can use `$a` repeatedly: `echo "$a is the count, the count is $a"; ((a += 4)); echo "Now it's $a (7)"`). – Dennis Williamson Jun 24 '22 at 20:41
  • You can do assignments in expansions. You would get both a variable set and the output. – Dennis Williamson Jun 24 '22 at 20:43
  • @RickO'Shea: In Bash, `[` and `test` are not external (regardless of the existence of external programs with those names in the filesystem). If you want to use those externals while in Bash, you have to do `/bin/[` or `env [`, for example. – Dennis Williamson Jun 29 '22 at 15:42
393
  1. A single bracket ([) usually actually calls a program named [; man test or man [ for more info. Example:

    $ VARIABLE=abcdef
    $ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
    yes
    
  2. The double bracket ([[) does the same thing (basically) as a single bracket, but is a bash builtin.

    $ VARIABLE=abcdef
    $ if [[ $VARIABLE == 123456 ]] ; then echo yes ; else echo no ; fi
    no
    
  3. Parentheses (()) are used to create a subshell. For example:

    $ pwd
    /home/user 
    $ (cd /tmp; pwd)
    /tmp
    $ pwd
    /home/user
    

    As you can see, the subshell allowed you to perform operations without affecting the environment of the current shell.

  4. (a) Braces ({}) are used to unambiguously identify variables. Example:

    $ VARIABLE=abcdef
    $ echo Variable: $VARIABLE
    Variable: abcdef
    $ echo Variable: $VARIABLE123456
    Variable:
    $ echo Variable: ${VARIABLE}123456
    Variable: abcdef123456
    

    (b) Braces are also used to execute a sequence of commands in the current shell context, e.g.

    $ { date; top -b -n1 | head ; } >logfile 
    # 'date' and 'top' output are concatenated, 
    # could be useful sometimes to hunt for a top loader )
    
    $ { date; make 2>&1; date; } | tee logfile
    # now we can calculate the duration of a build from the logfile
    

There is a subtle syntactic difference with ( ), though (see bash reference) ; essentially, a semicolon ; after the last command within braces is a must, and the braces {, } must be surrounded by spaces.

Gama11
  • 31,714
  • 9
  • 78
  • 100
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 29
    Well, `[` is actually a builtin in Bash, but it is supposed to act like `/bin/[` as opposed to the `[[` builtin. `[[` has different features, like more logical operations and different quoting roles. Additionally: single parentheses are also used for arrays, process substitution, and extended globs; double parentheses are used for arithmetic; curly braces `{}` are used for command grouping or multitudes of types of parameter expansion or brace expansion or sequence expansion. I'm sure I've missed some other uses too... – ephemient Feb 02 '10 at 22:46
  • 6
    The double-equals in the expression `if [ $VARIABLE == abcdef ]` is a bashism that -- although it works -- should probably be avoided; either explicitly use bash (`if [[ ...==...]]`) or make it clear that you're using the more traditional conditional ( `if [ "$VARIABLE" = "abcdef" ]` ). Arguably, scripts should start out as simple and portable as possible, up until they really do need features specific to bash (for one reason or another). But in any case, the intent should be clear; "=" and "==" and "[[" and "[" do work differently and their usage should be consistent. – michael Oct 06 '12 at 07:45
  • 3
    @michael_n: +1 for this remark. On a side note, I love scripting, but I find it quite awkward that the portable way is to test via `[ "$var" = ".."]` instead of `==` , whereas in C it would assign instead of testing (and is quite a common cause of bugs)... why didn't `test` use `==` instead of `=` ? anyone knows? – Olivier Dulac Oct 30 '13 at 11:05
  • Also here's a funny thing that *(at least on Kubuntu)* the command `/usr/bin/[` is not a symlink to the `/usr/bin/test`, and more: these programs even have a few different sizes! – Hi-Angel Sep 02 '14 at 07:19
  • Also: a single closing parenthesis `)` is part of the `case` statement syntax to end a case line. It does not have an opening parenthesis. This threw me off the first time I saw it. – Agustín Amenabar Jan 09 '18 at 07:35
  • do single braces require the surrounding whitespace, as shown? eg `{ date; top -b -n1 | head ; }` not `{date; top -b -n1 | head ;}`? – Anentropic Aug 05 '22 at 16:05
  • Yes. Bash 5.1 complains about that in a quick test here. – Carl Norum Aug 07 '22 at 03:26
344

Brackets

if [ CONDITION ]    Test construct  
if [[ CONDITION ]]  Extended test construct  
Array[1]=element1   Array initialization  
[a-z]               Range of characters within a Regular Expression
$[ expression ]     A non-standard & obsolete version of $(( expression )) [1]

[1] http://wiki.bash-hackers.org/scripting/obsolete

Curly Braces

${variable}                             Parameter substitution  
${!variable}                            Indirect variable reference  
{ command1; command2; . . . commandN; } Block of code  
{string1,string2,string3,...}           Brace expansion  
{a..z}                                  Extended brace expansion  
{}                                      Text replacement, after find and xargs

Parentheses

( command1; command2 )             Command group executed within a subshell  
Array=(element1 element2 element3) Array initialization  
result=$(COMMAND)                  Command substitution, new style  
>(COMMAND)                         Process substitution  
<(COMMAND)                         Process substitution 

Double Parentheses

(( var = 78 ))            Integer arithmetic   
var=$(( 20 + 5 ))         Integer arithmetic, with variable assignment   
(( var++ ))               C-style variable increment   
(( var-- ))               C-style variable decrement   
(( var0 = var1<98?9:21 )) C-style ternary operation
Loves Probability
  • 929
  • 1
  • 9
  • 15
Yola
  • 18,496
  • 11
  • 65
  • 106
  • @Yola, could you please explain what does $(varname) exactly? In Apple Xcode projects, I can specify file paths as script input/output. if I specify $SRC_ROOT/myFile.txt or ${SRC_ROOT}/myFile.txt (SRC_ROOT var is exported by the build system) - doesn't work. only $(SRC_ROOT)/myFile.txt works. What could be the reason? clearly var name isn't a command? – Motti Shneor Oct 31 '16 at 09:13
  • 2
    @MottiShneor, in your case `$(varname)` is unrelated to bash syntax. It is part of [Makefile syntax](//www.gnu.org/software/make/manual/html_node/Reference.html). – Sasha May 05 '17 at 14:21
  • Not so - Xcode is not building using makefile's and its variables are environment variables. Proprietary Xcode build-system processes reads the values of these predefined environment variables. Custom-build-steps are just normal shell-scripts (bash or other) and have access to the same vars. – Motti Shneor May 07 '17 at 11:32
  • 1
    @MottiShneor, ok, let's refine: most probably it is part of [xcconfig syntax](//pewpewthespells.com/blog/xcconfig_guide.html). Anyway, `$(varname)` has no relation to bash syntax in your case. – Sasha May 09 '17 at 00:35
  • Is there a difference between `(ls) &` and `{ls} &`? – nroose Oct 14 '21 at 22:28
24

I just wanted to add these from TLDP:

~:$ echo $SHELL
/bin/bash

~:$ echo ${#SHELL}
9

~:$ ARRAY=(one two three)

~:$ echo ${#ARRAY}
3

~:$ echo ${TEST:-test}
test

~:$ echo $TEST


~:$ export TEST=a_string

~:$ echo ${TEST:-test}
a_string

~:$ echo ${TEST2:-$TEST}
a_string

~:$ echo $TEST2


~:$ echo ${TEST2:=$TEST}
a_string

~:$ echo $TEST2
a_string

~:$ export STRING="thisisaverylongname"

~:$ echo ${STRING:4}
isaverylongname

~:$ echo ${STRING:6:5}
avery

~:$ echo ${ARRAY[*]}
one two one three one four

~:$ echo ${ARRAY[*]#one}
two three four

~:$ echo ${ARRAY[*]#t}
one wo one hree one four

~:$ echo ${ARRAY[*]#t*}
one wo one hree one four

~:$ echo ${ARRAY[*]##t*}
one one one four

~:$ echo $STRING
thisisaverylongname

~:$ echo ${STRING%name}
thisisaverylong

~:$ echo ${STRING/name/string}
thisisaverylongstring
kzh
  • 19,810
  • 13
  • 73
  • 97
  • 19
    Mind that `echo ${#ARRAY}` displays three, because of the first element of the `ARRAY` contains three characters, not because it contains three elements! To print the number of elements use `echo ${#ARRAY[@]}`. – TrueY Aug 01 '13 at 11:56
  • @zeal `${TEST:-test}` equals `$TEST` if the variable `TEST` exists, otherwise it simply returns the string "test". There is another version which does even more: `${TEST:=test}` --- which also equals to `$TEST` if TEST exists, but whenever it doesn't, it creates the variable `TEST` and assigns a value "test" and also becomes the value of the whole expression. – Loves Probability Jun 19 '16 at 04:07
22

The difference between test, [ and [[ is explained in great details in the BashFAQ. (Note: The link shows many examples for comparison)

To cut a long story short: test implements the old, portable syntax of the command. In almost all shells (the oldest Bourne shells are the exception), [ is a synonym for test (but requires a final argument of ]). Although all modern shells have built-in implementations of [, there usually still is an external executable of that name, e.g. /bin/[.

[[ is a new, improved version of it, and it is a keyword, not a program. This has beneficial effects on the ease of use, as shown below. [[ is understood by KornShell and BASH (e.g. 2.03), but not by the older POSIX or BourneShell.

And the conclusion:

When should the new test command [[ be used, and when the old one [? If portability/conformance to POSIX or the BourneShell is a concern, the old syntax should be used. If on the other hand the script requires BASH, Zsh, or KornShell, the new syntax is usually more flexible.

myrdd
  • 3,222
  • 2
  • 22
  • 22
fwhacking
  • 980
  • 8
  • 6
19

Parentheses in function definition

Parentheses () are being used in function definition:

function_name () { command1 ; command2 ; }

That is the reason you have to escape parentheses even in command parameters:

$ echo (
bash: syntax error near unexpected token `newline'

$ echo \(
(

$ echo () { command echo The command echo was redefined. ; }
$ echo anything
The command echo was redefined.
2

Additional info about How to use parentheses to group and expand expressions:
(it is listed on the link syntax-brackets)

Some main points in there:

Group commands in a sub-shell: ( )
(list)

Group commands in the current shell: { }
{ list; }

Test - return the binary result of an expression: [[ ]]
[[ expression ]]

Arithmetic expansion
The format for Arithmetic expansion is:
$(( expression ))

The format for a simple Arithmetic Evaluation is:
(( expression ))

Combine multiple expressions
( expression )
(( expr1 && expr2 ))

Toan NC
  • 2,960
  • 1
  • 14
  • 6
2

Some common and handy uses for brackets, parenthesis, and braces

As mentioned above, sometimes you want a message displayed without losing the return value. This is a handy snippet:

$ [ -f go.mod ] || { echo 'File not found' && false; }

This produced no output and a 0 (true) return value if the file go.mod exists in the current directory. Test the result:

$ echo $? 
0

If the file does not exist, you get the message but also a return value of 1 (false), which can also be tested:

$ [ -f fake_file ] || { echo 'File not found'; false; }
File not found

$ echo $?
1

You can also simply create a function to check if a file exists:

fileexists() { [ -f "$1" ]; }

or if a file is readable (not corrupted, have permissions, etc.):

canread() { [ -r "$1" ]; }

or if it is a directory:

isdir() { [ -d "$1" ]; }

or is writable for the current user:

canwrite() { [ -w "$1" ]; }

or if a file exists and is not empty (like a log file with content...)

isempty() { [ -s "$1" ]; }

There are more details at: TLDP


You can also see if a program exists and is available on the path:

exists () { command -v $1 > /dev/null 2>&1; }

This is useful in scripts, for example:

# gitit does an autosave commit to the current
# if Git is installed and available.
# If git is not available, it will use brew 
# (on macOS) to install it.
#
# The first argument passed, if any, is used as 
# the commit message; otherwise the default is used.
gitit() {
    $(exists git) && { 
        git add --all; 
        git commit -m "${1:-'GitBot: dev progress autosave'}"; 
        git push; 
    } || brew install git; 
}
Michael Treanor
  • 585
  • 8
  • 9