The interpretation between the braces in the brace expansion - bash

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.

Related

Why does quote in quoted variable expansion works well in 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

Why does field splitting not occur after parameter expansion in an assignment statement in shell?

Consider the following two assignments.
$ a="foo bar"
$ b=$a
$ b=foo bar
bash: bar: command not found
Why does the second assignment work fine? How is the second command any different from the third command?
I was hoping the second assignment to fail because
b=$a
would expand to
b=foo bar
Since $a is not within double-quotes, foo bar is not quoted, therefore field-splitting should occur (as per my understanding) which would result in b=foo to be considered an assignment and bar to be a command that cannot be found.
Summary: I was expecting the 2nd command to fail for the same reason that caused the 3rd command to fail. Why does the 2nd command succeed?
I went through the POSIX but I am unable to find anything that specifies that field splitting won't occur after parameter expansion that occurs in an assignment.
I mean anywhere else field splitting would occur for an unquoted parameter after parameter expansion. For example,
$ a="foo bar"
$ printf "[%s] [%s]\n" $a
[foo] [bar]
See Section 2.6.5.
After parameter expansion (Parameter Expansion), command substitution (Command Substitution), and arithmetic expansion (Arithmetic Expansion), the shell shall scan the results of expansions and substitutions that did not occur in double-quotes for field splitting and multiple fields can result.
So which part of the POSIX standard prevents field splitting when parameter expansion occurs in an assignment statement?
In 2.9.1, "Simple Commands":
The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing in steps 3 and 4.
Step 2 -- which is explicitly skipped in this case per the above text -- reiterates that it ignores assignments when performing expansion and field splitting:
The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.
Thus, it's step 2 that determines the command to run (based on contents other than variable assignments and redirections), which addresses the b=$a case given in your question.
Step 4 performs other expansions -- "tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal" -- for assignments. Notably, field splitting is not a member of this set. Indeed, it's explicit in 2.6 that none of these create multiple words in and of themselves:
Tilde expansions, parameter expansions, command substitutions, arithmetic expansions, and quote removals that occur within a single word expand to a single field. It is only field splitting or pathname expansion that can create multiple fields from a single word. The single exception to this rule is the expansion of the special parameter '#' within double-quotes, as described in Special Parameters.

How does `extension="${filename##*.}"` work in bash? [duplicate]

This question already has answers here:
What is the meaning of the ${0##...} syntax with variable, braces and hash character in bash?
(4 answers)
Closed 2 years ago.
While looking online on how to get a file's extension and name, I found:
filename=$(basename "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}
What is the ${} syntax...? I know regular expressions but "${filename##*.}" and "${filename%.*} escape my understanding.
Also, what's the difference between:
filename=$(basename "$fullfile")
And
filename=`basename "$fullfile"`
...?
Looking in Google is a nightmare, because of the strange characters...
The ${filename##*.} expression is parameter expansion ("parameters" being the technical name for the shell feature that other languages call "variables"). Plain ${varname} is the value of the parameter named varname, and if that's all you're doing, you can leave off the curly braces and just put $varname. But if you leave the curly braces there, you can put other things inside them after the name, to modify the result. The # and % are some of the most basic modifiers - they remove a prefix or suffix of the string that matches a wildcard pattern. # removes from the beginning, and % from the end; in each case, a single instance of the symbol removes the shortest matching string, while a double symbol matches the longest. So ${filename##*.} is "the value of filename with everything from the beginning to the last period removed", while ${filename%.*} is "the value of filename with everything from the last period to the end removed".
The backticks syntax (`...`) is the original way of doing command substitution in the Bourne shell, and has since been borrowed by languages like Perl and Ruby to incorporate calling out to system commands. But it doesn't deal well with nesting, and its attempt to even allow nesting means that quoting works differently inside them, and it's all very confusing. The newer $(...) syntax, originally introduced in the Korn shell and then adopted by Bash and zsh and codified by POSIX, lets quoting work the same at all levels of a nested substitution and makes for a nice symmetry with the ${...} parameter expansion.
As #e0k states in a comment on the question the ${varname...} syntax is Bash's parameter (variable) expansion. It has its own syntax that is unrelated to regular expressions; it encompasses a broad set of features that include:
specifying a default value
prefix and postfix stripping
string replacement
substring extraction
The difference between `...` and $(...) (both of which are forms of so-called command substitutions) is:
`...` is the older syntax (often called deprecated, but that's not strictly true).
$(...) is its modern equivalent, which facilitates nested use and works more intuitively when it comes to quoting.
See here for more information.

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.

Command substitution and field splitting in shell

I understand why the following command fails.
$ a=foo bar
-bash: bar: command not found
It attempts to first execute a=foo and then execute bar which fails because there is no such command called bar.
But I don't understand why this works. I was expecting the following command to fail as well.
$ a=$(echo foo bar)
$ echo "$a"
foo bar
As per http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 first command substitution happens, and then field splitting happens.
2.6 Word Expansions
This section describes the various expansions that are performed on
words. Not all expansions are performed on every word, as explained in
the following sections.
Tilde expansions, parameter expansions, command substitutions,
arithmetic expansions, and quote removals that occur within a single
word expand to a single field. It is only field splitting or pathname
expansion that can create multiple fields from a single word. The
single exception to this rule is the expansion of the special
parameter '#' within double-quotes, as described in Special
Parameters.
The order of word expansion shall be as follows:
Tilde expansion (see Tilde Expansion), parameter expansion (see Parameter Expansion), command substitution (see Command Substitution),
and arithmetic expansion (see Arithmetic Expansion) shall be
performed, beginning to end. See item 5 in Token Recognition.
Field splitting (see Field Splitting) shall be performed on the portions of the fields generated by step 1, unless IFS is null.
Pathname expansion (see Pathname Expansion) shall be performed, unless set -f is in effect.
Quote removal (see Quote Removal) shall always be performed last.
So after command subsitution,
a=$(echo foo bar)
becomes
a=foo bar
And then after to field splitting, a=foo should be executed first and then bar should be executed and then we should have the same error, i.e. bar: command not found. Why does a=$(echo foo bar) work fine then?
The answer is in 2.9.1 Simple Commands I believe.
Specifically points 1 and 4:
 1. The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing in steps 3 and 4.
 4. Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.
Or in the bash reference manual in 3.4 Shell Parameters:
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 (detailed below).

Resources