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.
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
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 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 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. (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.
[
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...
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.
[ "$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?
/usr/bin/[
is not a symlink to the /usr/bin/test
, and more: these programs even have a few different sizes!
)
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.
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
$(varname)
is unrelated to bash syntax. It is part of Makefile syntax.
$(varname)
has no relation to bash syntax in your case.
(ls) &
and {ls} &
?
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
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[@]}
.
${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.
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.
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.
unset -f echo
. See help unset
.
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;
}
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 ))
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
Success story sharing
:
.$[expression]
; this is the old, deprecated arithmetic expression syntax for the newer, preferred syntax:$((expression))
bash
is creating sequences, as peripherally mentioned below (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
echo {01..12..2}
-> "01 03 05 07 09 11". Thanks for the reminder about sequences. I'll add it to my answer.