When do we need curly braces around shell variables?
Asked Answered
S

7

987

In shell scripts, when do we use {} when expanding variables?

For example, I have seen the following:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

Is there a significant difference, or is it just style? Is one preferred over the other?

Sefton answered 5/1, 2012 at 19:54 Comment(1)
Related: nickjanetakis.com/blog/…Gennygeno
W
1116

In this particular example, it makes no difference. However, the {} in ${} are useful if you want to expand the variable foo in the string

"${foo}bar"

since "$foobar" would instead expand the variable identified by foobar.

Curly braces are also unconditionally required when:

  • expanding array elements, as in ${array[42]}
  • using parameter expansion operations, as in ${filename%.*} (remove extension; strips smallest match)
  • expanding positional parameters beyond 9: "$8 $9 ${10} ${11}"

Doing this everywhere, instead of just in potentially ambiguous cases, can be considered good programming practice. This is both for consistency and to avoid surprises like $foo_$bar.jpg, where it's not visually obvious that the underscore becomes part of the variable name.

Without answered 5/1, 2012 at 19:54 Comment(4)
@NewUser "So other than arrays it is not really required" Not so, the braces are necessary for PARAMETER EXPANSION, a very useful construct in scripting. I've seen many sed and awk scripts that can be replaced with a bit of parameter expansion.Vivyan
@karatedog ${1:-20} is a form of parameter expansion. Here it is not obvious because it mainly uses digits and arithmetic operators which trick us in thinking there is arithmetic involved, but it actually refers to the positional parameter $1, which if not defined will be replaced by a default value 20 (the syntax is ${variable:-default_value}).Bairn
In contrast to the example with the underscore: foo=Hello; echo $foo: results in Hello: and is the same as ${foo}:.Graziano
dealing with unset or empty variables is realized in curly braces, such as the "-" operator or the ":-" operator result in alternate results, especially when bash session is setup for faulting on empty vars by default, you are still able to tolerate it forcefully. see gnu.org/software/bash/manual/html_node/… and #14153034 e.g. a ${a-} sequence will gracefully set a non-existing variable to an empty string.Fluorescence
B
175

Variables are declared and assigned without $ and without {}. You have to use

var=10

to assign.

In order to read from the variable (in other words, expand the variable), you must use $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

This has confused me sometimes: in other languages we refer to the variable in the same way, regardless of whether it's on the left or right of an assignment. But shell-scripting is different, $var=10 doesn't do what you might think it does!

Bargello answered 5/1, 2012 at 20:5 Comment(0)
C
43

You use {} for grouping. The braces are required to dereference array elements. Example:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect
Ceylon answered 5/1, 2012 at 21:41 Comment(3)
I couldn't understand the first line dir=(*). As far as I know, dir is an in-built command to list directory contents (equivalent to ls -C -b). Could you please explain?Lutenist
In shell programming, commands and arguments must be separated from each other by whitespace. Here, you see the equal sign with no whitespace, meaning this is a variable assignment. dir is the name of the variable, and the parentheses are used to collect the filename expansion * into an array.Ceylon
@Lutenist In this case the word dir has no significance other then as a variable receiving an assignment. You can see this by using foo as the variable. foo=(*); echo "${foo[2]}"Whiteley
M
31

You are also able to do some text manipulation inside the braces:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Result:

./folder/subfolder/file.txt ./folder

or

STRING="This is a string"
echo ${STRING// /_}

Result:

This_is_a_string

You are right in "regular variables" are not needed... But it is more helpful for the debugging and to read a script.

Matronna answered 14/2, 2014 at 8:30 Comment(0)
W
24

Curly braces are always needed for accessing array elements and carrying out brace expansion.

It's good to be not over-cautious and use {} for shell variable expansion even when there is no scope for ambiguity.

For example:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

So, it is better to write the three lines as:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

which is definitely more readable.

Since a variable name can't start with a digit, shell doesn't need {} around numbered variables (like $1, $2 etc.) unless such expansion is followed by a digit. That's too subtle and it does make to explicitly use {} in such contexts:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

See:

Witte answered 21/3, 2019 at 18:33 Comment(5)
It's good to be not over-cautious: I wonder what most people think. Use curly braces all the time so you don't forget them when they're needed, or use them only where needed, to improve readability.Louden
I think it is the lack of awareness that leads to programmers using curlies even when they are not needed. This ignorance is similar to the other common mistake of not using double quotes to prevent inadvertent word splitting or globbing. At the base of it, the reality is that programmers are not serious about shell scripting as much as other scripting languages like Python and Ruby.Witte
True, that. My pet peeve is that everyone seems to think that all variables should be all caps in shell scripts :)Louden
I disagree with the "it's good to be not over-cautious" remark; it is absolutely better to be over-cautious. I'd much rather have a million unnecessary curly brackets than a mistake that breaks something, especially given how difficult it can be to find errors in shell scripts (unhelpful error messages, or no error at all).Partlet
@Partlet use shellcheckRichelle
M
18

The end of the variable name is usually signified by a space or newline. But what if we don't want a space or newline after printing the variable value? The curly braces tell the shell interpreter where the end of the variable name is.

Classic Example 1) - shell variable without trailing whitespace

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Example 2) Java classpath with versioned jars

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(Fred's answer already states this but his example is a bit too abstract)

Mumps answered 26/4, 2017 at 2:18 Comment(0)
K
4

Following SierraX and Peter's suggestion about text manipulation, curly brackets {} are used to pass a variable to a command, for instance:

Let's say you have a sposi.txt file containing the first line of a well-known Italian novel:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Ouput: quel ramo del lago di como che volge a mezzogiorno

Now create two variables:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Now substitute the word variable content with the one of new_word, inside sposi.txt file

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Ouput: quel filone del lago di como che volge a mezzogiorno

The word "ramo" has been replaced.

Kaliningrad answered 17/1, 2018 at 11:3 Comment(1)
This works just as well without curly braces around the variables.Gingergingerbread

© 2022 - 2024 — McMap. All rights reserved.