Suppose that parameter=value is an internal variable in bash.
When I try ${#parameter} in bash it returns a 5. But when I try, for instance, ${##parameter} or ${###parameter} it always give back 0 in return.
Why it does not say that it is a substitution error as in other cases?
Short version
You could express ${##parameter} as:
x=$#
${x#parameter}
and ${###parameter} as:
x=$#
${x##parameter}
You are performing a prefix removal on $# (number of arguments passed to a script / function).
Longer version
If parameter has any special meaning, it would be likely specific to your environment.
$ echo \""${parameter}"\"
""
or even:
$ set -u
$ echo \""${parameter}"\"
bash: parameter: unbound variable
Now with that out of the way, quick look into docs:
${#parameter}
The length in characters of the expanded value of parameter is substituted.
So with variable parameter not being set:
$ echo ${#parameter}
0
or with set -u:
$ echo ${#parameter}
bash: parameter: unbound variable
Additional # changed meaning to Remove matching prefix pattern. using: ${parameter#word} or ${parameter##word} syntax on ${#}, i.e. number of argument passed to a script/function. Good way to see and understand that behavior would be:
$ f() { echo -n "$# " ; echo ${##parameter}; }
$ f
0 0
$ f a b c
3 3
As you see in first call ${#} was 0 and (and attempting to strip prefix "parameter" does nothing to it really) and the second call value of ${#} is 3.
Prefix removal, the different between # and ## is, when matching patterns whether shorted or longest match is stripped.
Because ## is a special operator that removes a prefix. See for instance here: What is the meaning of the ${0##...} syntax with variable, braces and hash character in bash?
Related
So I got the following alias:
alias current_dir="pwd | sed -e 's/ /\\ /'"
In another place, I first safe my returned string in order to use parameter expansion to lowercase the string, like so:
CURRENT_DIR=$(current_dir)
echo "${CURRENT_DIR,,}"
But I wonder if it is possible to directly use parameter expansion on a alias/function call? I tried the following possibilities but they all didn't work for me:
echo "${current_dir,,}" # just an empty echo
echo "${$(current_dir),,}" # bad substitution
echo "${"$(current_dir)",,}" # bad substitution
No, it's not possible. You have to save the output in an intermediate variable. It's unavoidable.
You could use
declare -l CURRENT_DIR=$(current_dir)
Although Shellcheck has some sage words about declare and command substitution on the same line
However, to get a properly shell-quoted/escaped version of the string, use one of
$ mkdir '/tmp/a dir "with quotes and spaces"'
$ cd !$
$ printf -v CURRENT_DIR "%q" "$PWD"
$ echo "$CURRENT_DIR"
/tmp/a\ dir\ \"with\ quotes\ and\ spaces\"
$ CURRENT_DIR=${PWD#Q}
$ echo "$CURRENT_DIR"
'/tmp/a dir "with quotes and spaces"'
Get out of the habit of using ALLCAPS variable names, leave those as
reserved by the shell. One day you'll write PATH=something and then
wonder why
your script is broken.
${var#operator} showed up in bash 4.4:
${parameter#operator}
Parameter transformation. The expansion is either a transformation of the
value of parameter or information about parameter itself, depending on the
value of operator. Each operator is a single letter:
Q The expansion is a string that is the value of parameter quoted in
a format that can be reused as input.
E The expansion is a string that is the value of parameter with back-
slash escape sequences expanded as with the $'...' quoting mechan-
sim.
P The expansion is a string that is the result of expanding the value
of parameter as if it were a prompt string (see PROMPTING below).
A The expansion is a string in the form of an assignment statement or
declare command that, if evaluated, will recreate parameter with
its attributes and value.
a The expansion is a string consisting of flag values representing
parameter's attributes.
If parameter is # or *, the operation is applied to each positional param-
eter in turn, and the expansion is the resultant list. If parameter is an
array variable subscripted with # or *, the case modification operation is
applied to each member of the array in turn, and the expansion is the
resultant list.
I'd like to open bash and get the last positional parameter.
So I tried this one:
bash -cv 'echo $$#' -- 1 2 'The Third'
My idea is to use $# to get to know the number of arguments.
Because there are 3 arguments here - 1, 2 and 'The Third', $# should be 3 and $$# should become $3 and return 'The Third' to me.
But it turns out that $$ being evaluated first whose value is shell process id.
I searched for some while and I get this solution:
bash -cv 'eval echo \${$#}' -- 1 2 'The Third'
It works but it feels like I split one command into two.
Expansion first and eval second.
Is it possible to do it in one time command?
Like
bash -cv 'echo $($#)' -- 1 2 'The Third'
to set priority between expansions.
Your idea was okay, but that's not how you write it in bash syntax. Use "indirect expansion", introduced with the exclamation sign:
bash -cv 'echo ${!#}' -- 1 2 'The Third'
# => The Third
From man bash:
${parameter}
The value of parameter is substituted. The braces are required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character which is not to be interpreted as part of its name.
If the first character of parameter is an exclamation point, a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. The exceptions to this are the expansions of ${!prefix*} and ${!name[#]} described below. The exclamation point must immediately follow the left brace in order to introduce indirection.
You can do this in bash
bash -cv 'echo "${#: -1}"' -- 1 2 'The Third'
echo "${#: -1}"
The Third
Or you may use an array to store all arguments first:
bash -cv 'arr=("$#"); echo "${arr[#]: -1}"' -- 1 2 'The Third'
arr=("$#"); echo "${arr[#]: -1}"
The Third
I have a simple question but I wonder what is the difference between ${varname} and $varname ?
I use both but I don't see any difference which could tell me when to use one or the other.
Using {} in variable names helps get rid of ambiguity while performing variable expansion.
Consider two variables var and varname. Lets see you wanted to append the string name to the variable var. You can't say $varname because that would result in the expansion of the variable varname. However, saying ${var}name would help you achieve the desired result.
$ var="This is var variable."
$ varname="This is varname variable."
$ echo $varname
This is varname variable.
$ echo ${var}name
This is var variable.name
Braces are also required when accessing any element of an array.
$ a=( foo bar baz ) # Declare an array
$ echo $a[0] # Accessing first element -- INCORRECT
foo[0]
$ echo ${a[0]} # Accessing first element -- CORRECT
foo
Quoting from info bash:
Any element of an array may be referenced using ${name[subscript]}.
The braces are required to avoid conflicts with pathname expansion.
They are the same in a basic case, but using ${varname} gives more control and ability to work with the variable. It also skips edge cases in which it can create confusion. And finally, it enables variable expansion as described in Shell Parameter Expansion:
The ‘$’ character introduces parameter expansion, command
substitution, or arithmetic expansion. The parameter name or symbol to
be expanded may be enclosed in braces, which are optional but serve to
protect the variable to be expanded from characters immediately
following it which could be interpreted as part of the name.
When braces are used, the matching ending brace is the first ‘}’ not
escaped by a backslash or within a quoted string, and not within an
embedded arithmetic expansion, command substitution, or parameter
expansion.
The basic form of parameter expansion is ${parameter}. The value of
parameter is substituted. The braces are required when parameter is a
positional parameter with more than one digit, or when parameter is
followed by a character that is not to be interpreted as part of its
name.
Let's see a basic example. Here, the use of ${} allows us to do something that a simple $ does not. Consider we want to write $myvar + "blabla"::
$ myvar=23
$ echo $myvar
23
$ echo $myvarblabla
<--- the variable $myvarblabla doesn't exist!
$ echo ${myvar}blabla
23blabla
The distinction becomes important when something follows the variable:
text="House"
plural="${text}s"
Without the braces, the shell would see texts as variable name which wouldn't work.
The braces are also necessary when you use the extended syntax to specify defaults (${name-default}), display errors when undefined (${name?error}), or pattern substitution (see this article for other patterns; it's for BASH but most work for KSH as well)
> echo $name-default
-default
> echo ${name-default}
default
Related:
Parameter Substitution in Korn-/POSIX-Shell
I see it in someone's shell script file.
#!/bin/bash
var=...
echo "${!var}"
What does "!" mean here?
If the first character of parameter is an exclamation point (!), a
level of variable indirection is introduced. Bash uses the value of
the variable formed from the rest of parameter as the name of the
variable; this variable is then expanded and that value is used in the
rest of the substitution, rather than the value of parameter itself.
This is known as indirect expansion.
There are exceptions to this which are explained here
Example:
$ x=y
$ y=1
$ echo "${x}"
y
$ echo "${!x}"
1
I have a simple question but I wonder what is the difference between ${varname} and $varname ?
I use both but I don't see any difference which could tell me when to use one or the other.
Using {} in variable names helps get rid of ambiguity while performing variable expansion.
Consider two variables var and varname. Lets see you wanted to append the string name to the variable var. You can't say $varname because that would result in the expansion of the variable varname. However, saying ${var}name would help you achieve the desired result.
$ var="This is var variable."
$ varname="This is varname variable."
$ echo $varname
This is varname variable.
$ echo ${var}name
This is var variable.name
Braces are also required when accessing any element of an array.
$ a=( foo bar baz ) # Declare an array
$ echo $a[0] # Accessing first element -- INCORRECT
foo[0]
$ echo ${a[0]} # Accessing first element -- CORRECT
foo
Quoting from info bash:
Any element of an array may be referenced using ${name[subscript]}.
The braces are required to avoid conflicts with pathname expansion.
They are the same in a basic case, but using ${varname} gives more control and ability to work with the variable. It also skips edge cases in which it can create confusion. And finally, it enables variable expansion as described in Shell Parameter Expansion:
The ‘$’ character introduces parameter expansion, command
substitution, or arithmetic expansion. The parameter name or symbol to
be expanded may be enclosed in braces, which are optional but serve to
protect the variable to be expanded from characters immediately
following it which could be interpreted as part of the name.
When braces are used, the matching ending brace is the first ‘}’ not
escaped by a backslash or within a quoted string, and not within an
embedded arithmetic expansion, command substitution, or parameter
expansion.
The basic form of parameter expansion is ${parameter}. The value of
parameter is substituted. The braces are required when parameter is a
positional parameter with more than one digit, or when parameter is
followed by a character that is not to be interpreted as part of its
name.
Let's see a basic example. Here, the use of ${} allows us to do something that a simple $ does not. Consider we want to write $myvar + "blabla"::
$ myvar=23
$ echo $myvar
23
$ echo $myvarblabla
<--- the variable $myvarblabla doesn't exist!
$ echo ${myvar}blabla
23blabla
The distinction becomes important when something follows the variable:
text="House"
plural="${text}s"
Without the braces, the shell would see texts as variable name which wouldn't work.
The braces are also necessary when you use the extended syntax to specify defaults (${name-default}), display errors when undefined (${name?error}), or pattern substitution (see this article for other patterns; it's for BASH but most work for KSH as well)
> echo $name-default
-default
> echo ${name-default}
default
Related:
Parameter Substitution in Korn-/POSIX-Shell