ChatGPT解决这个技术问题 Extra ChatGPT

How to use double or single brackets, parentheses, curly braces

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?


D
Dennis Williamson

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

WARNING: That function is a fork bomb, do not run it. See: en.wikipedia.org/wiki/Fork_bomb
It's only a fork bomb if you invoke it with an additional :.
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))
@DennisWilliamson Another use of curly braces in 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
@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.
G
Gama11

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.


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...
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_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?
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!
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.
L
Loves Probability

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

@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?
@MottiShneor, in your case $(varname) is unrelated to bash syntax. It is part of Makefile syntax.
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.
@MottiShneor, ok, let's refine: most probably it is part of xcconfig syntax. Anyway, $(varname) has no relation to bash syntax in your case.
Is there a difference between (ls) & and {ls} &?
k
kzh

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

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[@]}.
@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.
m
myrdd

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.


p
pabouk - Ukraine stay strong

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.

Oh I tried in on the csh. My bad. When I try in on bash, it works. I didn't know the command 'command' of bash.
how can I cancel the redefinition of command echo()? (without reopening the bash)
@ChanKim: unset -f echo. See help unset.
M
Michael Treanor

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; 
}

T
Toan NC

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 ))


v
vuppala srikar
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

When you answer a question, do not just aim for "code", but also try to add an explanation ...
Did you really mean to just copy a portion of an above answer?