ChatGPT解决这个技术问题 Extra ChatGPT

When do we need curly braces around shell variables?

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?


5
5 revs, 4 users 50%

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)

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.


{} is known as brace expansion. ${} is known as variable expansion. They do different things. I'd upvote you except for the no expansion bit.
@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.
@caffinatedmonkey $() is used to execute a command, such that md5sum=$(md5sum foo.bin) will store the output of md5sum foo.bin in the variable md5sum, now accessible using ${md5sum}. Also, +1 and many more in spirit to OP for mentioning that it's good practice to be explicit!
@L0j1k Speaking of explicitness, I find it important to mention that $() executes its command from a subshell.
@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}).
B
Biclops

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!


g
glenn jackman

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

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?
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.
@Jarvis 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]}"
P
Peter Mortensen

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.


c
codeforester

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:

Allowed characters in Linux environment variable names


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.
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.
True, that. My pet peeve is that everyone seems to think that all variables should be all caps in shell scripts :)
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).
S
Sridhar Sarnobat

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)


u
user138278

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.


This works just as well without curly braces around the variables.
You may want to fix the weel-known novel bit. Upvoted nevertheless.