what's the meaning of "${0##*[\\/]}" in shell? - bash

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

Related

what does "// /_" mean in bash scripting [duplicate]

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.

Find and replace in shell script (special characters)

I am new to shell scripting:
I have following:
old=/dev/sda
new=/dev/sda5
Given these variables, I need to extract 5 from "new" string.
How should I go about it? sed? awk?
Tried using:
partitionno=$(echo $new | sed 's/$old//g')
To get the correct result with the least change to your command, try:
partitionno=$(echo "$new" | sed "s|$old||g")
There are two key points here:
Shell variables are not expanded inside single quotes. So '$old' remains as the original four characters: $, o, l, and d. For the shell variables to be expanded, use double quotes.
sed "s/$old//g" still won't work because there are too many slashes. A substitute command uses three slashes. After the shell expands $old, there are five slashes. The solution is to use a different delimiter for the substitute command. I chose | above because | is not likely to be in a file name.
Using Shell Parameter Expansion:
$ old=/dev/sda
$ new=/dev/sda5
$ echo "${new#$old}"
5
${parameter#word}
${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.

Bash script - Retrieve every value that fills *

I have a bash script that's like
for i in /path/to/file/*.in; do
./RunCode < "$i"
done
I want to be able to capture the output in something like *.out, with * being the same as *.in. How can I retrieve the text that * is expanded into so that I can reuse it?
By the wording in your question (could be clearer), I assume you wish remove the leading path.
You can use parameter expansion to accomplish what you want:
out_dir="/path/out"
for i in /path/to/file/*.in; do
name="${i##*/}"
./RunCode < "$i" > "$out_dir/${name%.in}.out"
done
This will remove the leading path and the .in extension, name all output files with .out extension, and place them in the directory /path/out.
${i##*/} - Removes all leading characters through the last occurrence of / to get the name of the file with the .in extension.
${name%.in}.out - Removes the trailing .in extension from name and replaces with .out.
Change file suffix with bash:
for i in /path/to/file/*.in; do
./RunCode < "$i" > "${i%.in}.out"
done
From man bash:
${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.

clarification with a shell script

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.

Shell script parameter substitution

Can someone tell me what the difference is between these two conventions for getting the name of the currently running script?
#!/bin/sh
echo $0
echo ${0##*/}
Does the second version strip the path from the script name?
Thanks!
Your guess is correct. From the bash docs on my machine:
${parameter#word}
${parameter##word}
The word is expanded to produce a pattern just as in pathname
expansion. If the pattern matches the beginning of the 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.
Yes, second version uses bash extension to cut longest prefix of $0 which match to */ regexp, so if $0 is "/bin/bash", then ${0##*/} will be "bash". You can use `basename $0` to achieve the same result.

Resources