Why does quote in quoted variable expansion works well in bash? - bash

I am learning quote in bash.
I got a code
unset unset_var
test_var="${unset_var:-"abc"}"
echo "test_var = $test_var"
tset_var = abc
My question comes from the line, "${unset_var:-"abc"}"
I interupted the line in two ways,
The first ways is
"${unset_var:-"abc"}" =
(quoted string: "${unset_var:-") +
(unquoted string: abc) +
(quoted string: "}")
The second way is
"${unset_var:-"abc"}" =
("${}") + (abc:-"abc")
The first way is intuitive for me.
The second way is similar to independent quote in sub-shell from parent-shell, like
"$(command "aug")" # quote in sub-shell is independent from one in parent-shell
I could not find a instruction of this question in bash manual.
Someone who knows how it works, please let me know. Thank you.

In any assignment statement of the form
name=value
value undergoes quote-removal, which is the removal of any quotes that are not a result of expansions applied to value.
With
test_var="${unset_var:-"abc"}"
the quotes around the parameter expansion are clearly not the result of any expansions, so they are removed. The question is, how are the inner quotes treated?
According to the man page,
In [${parameter:-word}], word is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion."
However, since "abc" does not undergo any of the four named expansions, the surrounding quotes are not the product of an expansion, and so are removed per quote removal. Thus,
test_var="${unset_var:-"abc"}"
is equivalent to
test_var="${unset_var:-abc}"
which is equivalent to
test_var=abc

Related

How to add $USER inside of nested quotes? [duplicate]

This question already has answers here:
Difference between single and double quotes in Bash
(7 answers)
Closed last year.
I have a variable:
my_var="$('/some/path/to/username/more/path' 'and/something/else')"
and I want to change it to be more generic like:
my_var="$('/some/path/to/${USER}/more/path' 'and/something/else')"
but that doesn't work. How can I get the user inside of there?
The problem is that inside a single quoted string nothing is treated with a special meaning. This makes them good for scripting, eg. grep '^pattern$' ... and sed 's/abc$/def/'.
You can use double quotes instead.
$(...) is a command substitution. Is that correct? If so, then you can nest double qoutes:
my_var="$("/some/path/to/${USER}/more/path" 'and/something/else')"
This should be interpreted as two set of quotes, so the outer double quotes, isn't stopped at the first inner quote:
my_var="$( )" # First set of quotes
"/some/path/to/${USER}/more/path" # Second set of quotes
When assigning a variable, you don't need to wrap the expression in double quotes, so:
my_var=$("/some/path/to/${USER}/more/path" 'and/something/else')
would also be fine in this case. Most other cases you should always wrap parameter expansions ($abc), command substitutions ($(...)) etc. in double quotes as to avoid word splitting and pathname expansions.
However to me it seems like you are trying to create an array instead?
$ my_var=("/some/path/to/${USER}/more/path" 'and/something/else')
$ echo "${my_var[0]}"
/some/path/to/and/more/path

How to use double quotes when assigning variables?

There's a bash file with something like this:
FOO=${BAR:-"/some/path/with/$VAR/in/it"}
Are those double quotes necessary? Based on the following test, I'd say no, and that no quote at all is needed in the above assignment. In fact, it's the user of that variable that needs to expand it within double quotes to avoid wrong splitting.
touch 'some file' # create a file
VAR='some file' # create a variable for that file name
FOO=${BAR:-$VAR} # use it with the syntax above, but no quotes
ls -l "$FOO" # the file does exist (here we do need double quotes)
ls -l $FOO # without quotes it fails searching for files `some` and `file`
rm 'some file' # remove temporary file
Am I correct? Or there's something more?
Are those double quotes necessary?
Not in this case, no.
Am I correct?
Yes. And it's always the user of the variable that has to quote it - field splitting is run when expanding the variable, so when using it it has to be quoted.
There are exceptions, like case $var in and somevar1=$somevar2 - contexts which do not run field splitting, so like do not require quoting. But anyway, quotes do not hurt in such cases and can be used anyway.
Or there's something more?
From POSIX shell:
2.6.2 Parameter Expansion
In addition, a parameter expansion can be modified by using one of the following formats. In each case that a value of word is needed (based on the state of parameter, as described below), word shall be subjected to tilde expansion, parameter expansion, command substitution, and arithmetic expansion.
${parameter:-word}
Because field splitting expansion is not run over word inside ${parameter:-word}, indeed, quoting doesn't do much.

The interpretation between the braces in the brace expansion

In Bash Beginners Guide Book
Brace expansion is performed before any other expansions, and any characters special to other expansions are
preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of
the expansion or the text between the braces. To avoid conflicts with parameter expansion, the string "${" is
not considered eligible for brace expansion.
In This paragraph it says that the Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces, but when I executed this command
h{elp,`uname`}
It returned
bash: help: no help topics match `hLinux'. Try `help help' or `man -k hLinux' or `info hLinux'.
It retuend the word hLinux instead of h`uname`.
So the `uname` is interpreted even when it was between the braces, why ?
Bash Beginners Guide Screenshot of the space paragraph
The brace expansion in your example did not return hLinux. It returned help h`uname`.
Only in a second step, Bash applied command substitiution to `uname`, which made the entire command help hLinux.
Brace expansion does not stop other mechanisms to be applied on the result afterwards. It just does not parse anything by itself.

Bash escaping/expanding order

I'm fairly new to Bash and I'm having trouble working out what is happening to my input as it is interpreted. Specifically, when escaping occurs relative to the other expansion steps.
From what I've read, bash does the following (in order):
brace expansion
tilde expansion
parameter and variable expansion
command substitution
arithmetic expansion
word splitting
filename expansion
But this list doesn't include when it converts all escape sequences e.g. '\\' into their meanings e.g. '\'. That is, if I want to print a backslash character. The command to run is
echo \\
not
echo \
So the syntax required for the semantics of a backslash character is two backslashes. This must be converted into a single slash representation internally.
It seems to be sometime before command substitution as I found out with a small test program.
So, my question is: When does this step take place? (or a complete list of the bash interpretation loop would be perfect)
and also, are there any other subtleties in the interpreter that are likely to catch me out? (related to knowing the complete list i guess)
From the man page's Expansion section, just before the Redirection section.
Quote Removal
After the preceding expansions, all unquoted occurrences of the characters \, ', and " that did not result from one of the above expansions
are removed.
Quote removal is one final process after the seven expansions you list.

using double quotes in bash export statement

Hello I'm reading a book about bash scripting and the author says to add the following to the end of my .bashrc file. export PATH=~/bin:"$PATH" in order to execute my file from the command line by typing its name. I notice however that if I put export PATH=~/bin:$PATH I can achieve the same result. So my question is what is the difference between the one with quotes and the one without quotes? thanks.
The quotes won't hurt anything, but neither are they necessary. Assignments are processed specially by the shell. From the man page:
A variable may be assigned to by a statement of the form
name=[value]
If value is not given, the variable is assigned the null string. All values undergo tilde expansion, parameter and variable
expansion, command substitution, arithmetic expansion, and
quote removal (see EXPANSION below).
Notice that word-splitting and pathname generation are not on the list in bold. These are the two types of expansion you are trying to prevent by quoting a parameter expansion, but in this context they are not performed. The same rules apply to the assignments that are passed to the export built-in command.
You must include the variable PATH inside double quotes. So that it would handle the filepaths which has spaces but without double quotes, it won't handle the filenames which has spaces in it.
I was facing the same with trying to assign a JSON string to a variable in the terminal.
Wrap it with Single Quotes or Double Quotes
Use single quotes, if you string contains double quotes and vice-versa.
$ export TEMP_ENV='I like the "London" bridge'
$ echo $TEMP_ENV
>> I like the "London" bridge
$ export TEMP_ENV="I like the 'London' bridge"
$ echo $TEMP_ENV
>> I like the 'London' bridge

Resources