In the golang GOPATH documentation it instructs you to add the following to your bash PATH:
${GOPATH//://bin:}/bin
What does the //://bin: mean here?
$GOPATH appears to evaluate to the same value as ${GOPATH//://bin:}
My first guess was that this somehow expands into all subdirectories of $GOPATH that have a /bin subdirectory, but this does not appear to be the case. I added some subdirectories to my $GOPATH with a /bin dir, and the above expression does not include them.
I dug into my manpage for bash and didn't see any hints.
man bash says under PARAMETER EXPANSION:
${parameter/pattern/string}
Pattern substitution. The pattern is expanded to produce a pattern just as in
pathname 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.
e.g.
$ GOPATH=a:b:c
$ echo ${GOPATH//://bin:}
a/bin:b/bin:c
Related
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.
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
How can I match environment variables which include the case-insensitive segment "proxy" that is not a prefix? I'm on bash:
root#PDPINTDEV9:~# echo ${SHELL}
/bin/bash
I want to unset a bunch of proxy variables simultaneously. They all have "proxy" or "PROXY" in the name, such as http_proxy or NO_PROXY. I would like to use glob expansion, which this answer & comment says is what bash uses.
Also based on that answer, I see that I can find environment vars which start with "PROXY":
root#PDPINTDEV9:~# echo "${!PROXY*}"
PROXY_IP PROXY_PORT
But that doesn't make sense with what I've read about glob expansion. Based on those, "${!PROXY*}" should match anything that doesn't start with proxy... I think.
Furthermore, I can't get anything that does make sense with glob syntax to actually work:
root#PDPINTDEV9:~# echo ${*proxy}
-bash: ${*proxy}: bad substitution
root#PDPINTDEV9:~# echo "${!*[pP][rR][oO][xX][yY]}"
-bash: ${!*[pP][rR][oO][xX][yY]}: bad substitution
SOLVED below: Turns out you can't. Crazy, but thanks everyone.
Variable name expansion, as a special case of shell parameter expansion, does not support globbing. But it has two flavors:
${!PREFIX*}
${!PREFIX#}
In both, the * and # characters are hard-coded.
The first form will expand to variable names prefixed with PREFIX and joined by the first character of the IFS (which is a space, by default):
$ printf "%s\n" "${!BASH*}"
BASH BASHOPTS BASHPID BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND ...
The second form will expand to variable names (prefixed with PREFIX), but as separate words:
$ printf "%s\n" "${!BASH#}"
BASH
BASHOPTS
BASHPID
BASH_ALIASES
BASH_ARGC
...
Both of these forms are case-sensitive, so to get the variable names in a case-insensitive manner, you can use set, in combination with some cut and grep:
$ (set -o posix; set) | cut -d= -f1 | grep -i ^proxy
PROXY_IP
proxy_port
But that doesn't make sense with what I've read about glob expansion.
Based on those, "${!PROXY*}" should match anything that doesn't start
with proxy... I think.
No and no.
In the first place, the ! character is not significant to pathname expansion, except when it appears at the beginning of a character class in a pattern, in which case the sense of the class is inverted. For example, fo[!o] is a pattern that matches any three-character string whose first two characters are "fo" and whose third is not another 'o'. But there is no character class in your expression.
But more importantly, pathname expansion isn't relevant to your expression ${!PROXY*} at all. There is no globbing there. The '!' and '*' are fixed parts of the syntax for one of the forms of parameter expansion. That particular expansion produces, by definition, the names of all shell variables whose names start with "PROXY", separated by the first character of the value of the IFS variable. Where it appears outside of double quotes, it is equivalent to ${!PROXY#}, which is less susceptible to globbing-related confusion.
Furthermore, I can't get anything that does make sense with glob syntax to actually work: [...]
No, because, again, there is no globbing going on. You need exactly ${! followed by the name prefix of interest, followed by *} or #} to form the particular kind of parameter expansion you're asking about.
How can I match environment variables which include the case-insensitive segment "proxy"?
You need to explicitly express the case variations of interest to you. For example:
${!PROXY*} ${!proxy*} ${!Proxy*}
Say I have the paths
a/b/c/d/e/f
a/b/c/d
How do I get the below?
e/f
You can strip one string from the other with:
echo "${string1#"$string2"}"
See:
$ string1="a/b/c/d/e/f"
$ string2="a/b/c/d"
$ echo "${string1#"$string2"}"
/e/f
From man bash -> Shell parameter expansion:
${parameter#word}
${parameter##word}
The word is expanded to produce a pattern just as in 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.
With spaces:
$ string1="hello/i am here/foo/bar"
$ string2="hello/i am here/foo"
$ echo "${string1#"$string2"}"
/bar
To "clean" multiple slashes, you can follow Roberto Reale's suggestion and canonicalize the paths with readlink -m to allow comparison with strings with the same real path up:
$ string1="/a///b/c//d/e/f/"
$ readlink -m $string1
/a/b/c/d/e/f