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?
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.
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!
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
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.
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
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)
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.
I have 2 variables that I want to use to derive a 3rd variable:
export REGION_NAME=phx
export phx_url=https://www.google.com
I am trying to do the following:
echo "$((${REGION_NAME}_url))"
And I get the following error:
-sh: https://www.google.com: syntax error in expression (error token is "://www.google.com")
All I am trying to do is to derive an environment variable from an other one but it does not work simple like that. I think it has to be escaped and could not find anything online.
Thanks in advance for the help.
$((...)) is arithmetic expansion. You didn't mean that. Try normal variable expansion (with indirection) instead.
REGION_NAME=phx
phx_url=https://www.google.com
R_VAR=${REGION_NAME}_url
echo "${!R_VAR}"
One possible solution is using eval like:
var="phx"
eval "${var}_url='some'"
echo $phx_url #prints "some"
But, I not recommending this (because the eval could be pretty dangerous).
Instead of use associative arrays (aka hash variable), like:
declare -A urls
var="phx"
urls[$var]="some2"
echo "${urls[phx]}" #prints "some2"
I don't know exactly how to ask this in English, but I want to have the value of a variable as a new variable...
The script also has a loop with increasing numbers, and in the end I want to have the variables VAR1, VAR2 etc.
I'm trying this:
COUNT=$(echo 1)
DEFINE=$(echo VAR$COUNT)
$DEFINE=$(echo gotcha!)
When I try this way, I have this error message:
~/script.sh: line n: VAR1=gotcha!: command not found
I played a bit around with brackets and quotation marks, but it didn't work... any solutions?
The problem is that bash expects a command as a result of expansions, not an assignment. VAR1=gotcha! is not a command, hence the error.
It would be better to use an array:
COUNT=$(echo 1)
VAR[COUNT]='gotcha!'
echo ${VAR[COUNT]}
I guess $(echo 1) stands for a more complex command, otherwise you can just use COUNT=1.
You can use declare to create such a "dynamic" variable, but using an array is probably a better choice.
COUNT=1
DEFINE="VAR$COUNT"
declare "$DEFINE=gotcha"
I have variables that look something like this:
$INFOMonday
$INFOTuesday
In my Bash script, I would like to use the value of todays variable, so to say. I extracted the day with:
dayofweek=$(date --date=${dateinfile#?_} "+%A")
What I need help with, is doing the following:
echo $INFO$dayofweek
Is there any way of adding a variable as part of a variables name? For example, if it's monday, the echo would return the value of $INFOMonday.
The old-style way of doing this is with the indirection operator ${!variable}:
dayofweek=$(date...)
var=INFO$dayofweek
echo ${!var}
Note that in ${!var}, var must be a variable-name, not a string expression or other type of substitution. It's a little clunky.
The newer way of doing it would be with an associative array.
I've seen a couple posts for this, like this one, but none are helping me in my particular situation.
scriptsPath="/var/db/gbi/scripts/"
echo "$scriptsPathawesome.csv";
I would expect this to echo /var/db/gbi/scripts/awesome.csv
Instead I get .csv
Seems like it thinks I'm trying to reference a variable named $scriptsPathawesome. How can I concatenate the $scriptsPath variable to the "awesome.csv" string literal?
You need to surround your variable with curly braces like so:
scriptsPath="/var/db/gbi/scripts/"
echo "${scriptsPath}awesome.csv";