This question already has an answer here:
What is the meaning of `//` in Bash parameter expansions?
(1 answer)
Closed last year.
I am currently moving our shell/bash scripts from a jenkinsfile to groovy scripts that are stored in methods but still execute as sh scripts.
The issue i have is with variables containing // /_
exmaple:
${VARIABLE_NAME// /_}
I cannot find what // /_ exactly does when supplied like this in a variable.
I need to find another way to do this because when moved to Groovy methods, it causes formating issues where escaping doesnt work properly.
It will replace all spaces with underscores.
Consider the following example:
$ var='hello world john doe'
$ echo "${var// /_}"
hello_world_john_doe
$
${parameter/pattern/string}
The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. The match is performed according to the rules described below (see Pattern Matching). If pattern begins with ‘/’, all matches of pattern are replaced with string. Normally only the first match is replaced. If pattern begins with ‘#’, it must match at the beginning of the expanded value of parameter. If pattern begins with ‘%’, it must match at the end of the expanded value of parameter. If string is null, matches of pattern are deleted and the / following pattern may be omitted. If the nocasematch shell option (see the description of shopt in The Shopt Builtin) is enabled, the match is performed without regard to the case of alphabetic characters. If parameter is ‘#’ or ‘’, the substitution operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with ‘#’ or ‘’, the substitution operation is applied to each member of the array in turn, and the expansion is the resultant list.
Copied from: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html with my emphasize on how / and // differences.
Related
I'll start with the two motivating examples, to give proper context for the question, and then ask the question. First consider this example:
$ ext=.mp3
$ fname=file.mp3
$ echo ${fname%"$ext"}
file
Evidently, in parsing ${fname%"$ext"}, bash first expands $ext into .mp3, and then expands ${fname%.mp3} into file — the last step follows trivially from the definition of % expansions. What's confusing me is the expansion of $ext...
In particular, let's compare the above with this similar example:
$ a=value
$ b=a
$ echo ${$b}
-bash: ${$b}: bad substitution
Of course, I know I could use "indirect expansion" here to achieve what I want:
$ echo ${!b}
value
But that's not relevant to my question. I want to understand the specific bash evaluation and parsing rules that explain why ${$b} fails but ${fname%"$ext"} succeeds.
The only relevant passage I've found in man bash is this:
The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.
But I'm not seeing how the different behaviors result from these rules.
I'd like see an explanation that explains each step of the evaluation process of the two examples, and the rule underlying each step.
If you look up ${parameter%word} expansion in the bash manual you'll see that parameter and word are treated differently. word is subject to pathname expansion while parameter is not.
${parameter%word}
${parameter%%word}
Remove matching suffix pattern. The word is expanded to produce a
pattern just as in pathname expansion. If the pattern matches a
trailing portion of the expanded value of parameter, then the result
of the expansion is the expanded value of parameter with the shortest
matching pattern (the % case) or the longest matching pattern (the
%% case) deleted. If parameter is # or *, the pattern removal
operation is applied to each positional parameter in turn, and the
expansion is the resultant list. If parameter is an array variable
subscripted with # or *, the pattern removal operation is applied to
each member of the array in turn, and the expansion is the resultant
list.
That seems like it would explain it. But it doesn't. Pathname expansion only means globbing and pattern matching with *, ?, and the like. It doesn't include variable expansion.
The key is to read up. There's a preamble that applies to the above:
In each of the cases below, word is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion.
In totality, word is subject to all of these expansions. Key to this question: $ext is expanded via recursive parameter expansion.
I say "recursive" because it can in fact be nested arbitrarily deep. To wit:
$ echo ${fname%.mp3}
file
$ echo ${fname%"$ext"}
file
$ echo ${fname%"${ext%"$empty"}"}
file
$ echo ${fname%"${ext%"${empty%""}"}"}
file
I got the question when i looking other's shell script.
I saw that declared
APP_NAME="${0##*[\\/]}"
Since I can't find any answer, what's the meaning of this code?
It's Shell Parameter Expansion to get script name itself, without path
See bash manual:
${parameter##word}
The word is expanded to produce a pattern and matched according to the rules described below (see Pattern Matching).
If the pattern matches the beginning of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the ‘#’ case) or the longest matching pattern (the ‘##’ case) deleted.
Pattern Matching:
*
Matches any string, including the null string. When the globstar shell option is enabled, and ‘*’ is used in a filename expansion context
[…]
Matches any one of the enclosed characters.
Explanation
${parameter<...>} expression means that you can expand shell parameters.
I.e. ${1:-"default_arg_value"} will be expanded to "default_arg_value" if script running without arguments.
0 - is a 0th argument, i.e. script name itself
${0##<pattern>} will delete longest matching to <pattern> part of $0
*[\\/] means any string that ends with \ or / symbol.
So, APP_NAME="${0##*[\\/]}" means that $APP_NAME will be initialized by script name itself, without path.
Sample
Let's suppose you have script a/b/c/d/test.sh:
#!/bin/bash
echo "${0##*[\/]}"
echo "${1#*[\/]}"
echo "${2##[\/]}"
$ bash a/b/c/d/test.sh /tmp/e /tmp/ff
> test.sh
> tmp/e
> tmp/ff
I am attempting to write a bash script (poorly) and need assistance in stripping characters from a variable.
variable is defined as $managementipmask= 111.111.111.111/24
I need to strip the /24 from the end of the variable.
Thanks in advance.
Use parameter expansion to remove everything from the first /:
$ k="111.111.111.111/24"
$ echo "${k%%/*}"
111.111.111.111
See this resource on parameter expansion for additional details:
http://mywiki.wooledge.org/BashGuide/Parameters#Parameter_Expansion
${parameter%pattern}
The 'pattern' is matched against the end of 'parameter'. The result is
the expanded value of 'parameter' with the shortest match deleted.
${parameter%%pattern}
As above, but the longest match is deleted.
So you can delete from the last / using a single %:
$ k="111.111.111.111/24/23"
$ echo "${k%/*}"
111.111.111.111/24
Another way:
k="111.111.111.111/24"
echo "${k/%\/24/}"
It replaces last /24 with empty string.
From Bash Manual:
${parameter/pattern/string}
The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern
against its value is replaced with string. If pattern begins with ‘/’,
all matches of pattern are replaced with string. Normally only the
first match is replaced. If pattern begins with ‘#’, it must match at
the beginning of the expanded value of parameter. If pattern begins
with ‘%’, it must match at the end of the expanded value of parameter.
If string is null, matches of pattern are deleted and the / following
pattern may be omitted. If parameter is ‘#’ or ‘’, the substitution
operation is applied to each positional parameter in turn, and the
expansion is the resultant list. If parameter is an array variable
subscripted with ‘#’ or ‘’, the substitution operation is applied to
each member of the array in turn, and the expansion is the resultant
list.
Can somebody explain how echo "${PWD/#$HOME/~}" would print ~ in case the PWD evaluates to $HOME. Never read about such replacement using echo. What is going on here?
It is not echo it is your shell makes Parameter Expansion using ${parameter/pattern/string} syntax:
The pattern is expanded to produce a pattern just as in filename
expansion. Parameter is expanded and the longest match of pattern
against its value is replaced with string. If pattern begins with ‘/’,
all matches of pattern are replaced with string. Normally only the
first match is replaced. If pattern begins with ‘#’, it must match at
the beginning of the expanded value of parameter. If pattern begins
with ‘%’, it must match at the end of the expanded value of parameter.
If string is null, matches of pattern are deleted and the / following
pattern may be omitted. If parameter is ‘#’ or ‘*’, the substitution
operation is applied to each positional parameter in turn, and the
expansion is the resultant list. If parameter is an array variable
subscripted with ‘#’ or ‘*’, the substitution operation is applied to
each member of the array in turn, and the expansion is the resultant
list.
It doesn't look like POSIX supports it.
In your case, it replaces the value of $HOME envvar (not the string '$HOME' literally) with ~ in the output if PWD envvar starts with it.
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.
I'm looking through a bash script I've inherited and come across this line:
local n=${x##*wlan}
I've never seen this syntax, I know that local means a variable in the scope of a function, I'm just unsure about the ${*##} part and it's pretty tricky to google that syntax.
It removes everything up to the last wlan match in the $x variable.
asldkjflkasjdfljsdwlanalsdkjfkajsdflswlanasdlfaksdlfj
# ^^^^ ^^^^
# | |--->
# | ${x##*wlan}
# ${x#*wlan}
See an example:
$ x="hello1hello2hello3"
$ echo "${x##*hello}" # with two ## it matches the longest matching pattern
3
$ echo "${x#*hello}" # with one # it matches the shortest matching pattern
1hello2hello3
Graphically:
hello1hello2hello3
# ^
# ^ ${x##*hello}
# ${x#*hello}
From Bash Reference Manual - 3.5.3 Shell Parameter Expansion:
${parameter##word}
The word is expanded to produce a pattern just as in filename
expansion (see Filename Expansion). If the pattern matches the
beginning of the expanded value of parameter, then the result of the
expansion is the expanded value of parameter with the shortest
matching pattern (the ‘#’ case) or the longest matching pattern (the
‘##’ case) deleted. If parameter is ‘#’ or ‘’, the pattern removal
operation is applied to each positional parameter in turn, and the
expansion is the resultant list. If parameter is an array variable
subscripted with ‘#’ or ‘’, the pattern removal operation is applied
to each member of the array in turn, and the expansion is the
resultant list.
It's called parameter substitution/expansion, see here for more information.
Your example removes the longest match of *wlan from the beginning of the variable $x.