Bash last index of - bash

Sorry for the lame bash question, but I can't seem to be able to work it out.
I have the following simple case:
I have variable like artifact-1.2.3.zip
I would like to get a sub-string between the hyphen and the last index of the dot (both exclusive).
My bash skill are not too strong. I have the following:
a="artifact-1.2.3.zip"; b="-"; echo ${a:$(( $(expr index "$a" "$b" + 1) - $(expr length "$b") ))}
Producing:
1.2.3.zip
How do I remove the .zip part as well?

The bash man page section titled "Variable Substitution" describes using ${var#pattern}, ${var##pattern}, ${var%pattern}, and ${var%%pattern}.
Assuming that you have a variable called filename, e.g.,
filename="artifact-1.2.3.zip"
then, the following are pattern-based extractions:
% echo "${filename%-*}"
artifact
% echo "${filename##*-}"
1.2.3.zip
Why did I use ## instead of #?
If the filename could possibly contain dashes within, such as:
filename="multiple-part-name-1.2.3.zip"
then compare the two following substitutions:
% echo "${filename#*-}"
part-name-1.2.3.zip
% echo "${filename##*-}"
1.2.3.zip
Once having extracted the version and extension, to isolate the version, use:
% verext="${filename##*-}"
% ver="${verext%.*}"
% ext="${verext##*.}"
% echo $ver
1.2.3
% echo $ext
zip

$ a="artifact-1.2.3.zip"; a="${a#*-}"; echo "${a%.*}"
‘#pattern’ removes pattern so long as it matches the beginning of $a.
The syntax of pattern is similar to that used in filename matching.
In our case,
* is any sequence of characters.
- means a literal dash.
Thus #*- matches everything up to, and including, the first dash.
Thus ${a#*-} expands to whatever $a would expand to,
except that artifact- is removed from the expansion,
leaving us with 1.2.3.zip.
Similarly, ‘%pattern’ removes pattern so long as it matches the end of the expansion.
In our case,
. a literal dot.
* any sequence of characters.
Thus %.* is everything including the last dot up to the end of the string.
Thus if $a expands to 1.2.3.zip,
then ${a%.*} expands to 1.2.3.
Job done.
The man page content for this is as follows (at least on my machine, YMMV):
${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.
${parameter%word}
${parameter%%word}
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 pat-
tern (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.
HTH!
EDIT
Kudos to #x4d for the detailed answer.
Still think people should RTFM though.
If they don't understand the manual,
then post another question.

Using Bash RegEx feature:
>str="artifact-1.2.3.zip"
[[ "$str" =~ -(.*)\.[^.]*$ ]] && echo ${BASH_REMATCH[1]}

I think you can do this:
string=${a="artifact-1.2.3.zip"; b="-"; echo ${a:$(( $(expr index "$a" "$b" + 1) - $(expr length "$b") ))}}
substring=${string:0:4}
The last step removes the last 4 characters from the string. There's some more info on here.

Related

What is the meaning of ${var#?} in a shell script

I don't kanow what #? mean, I goolged,found nothing
the full file: tinode/chat
goplat=( darwin windows linux )
# Supported CPU architectures: amd64
goarc=( amd64 )
# Supported database tags
dbtags=( mysql mongodb rethinkdb )
for line in $#; do
eval "$line"
done
version=${tag#?}
if [ -z "$version" ]; then
# Get last git tag as release version. Tag looks like 'v.1.2.3', so strip 'v'.
version=`git describe --tags`
version=${version#?}
fi
${tag#?} expands to the value of $tag with the first character deleted.
Quoting the POSIX shell specification:
${parameter#[word]}
Remove Smallest Prefix Pattern. The word shall be
expanded to produce a pattern. The parameter expansion shall then
result in parameter, with the smallest portion of the prefix matched
by the pattern deleted. If present, word shall not begin with an
unquoted '#'.
In this case the pattern is ?, which matches a single character.
If you're using bash, the Bash manual also covers this (you can view the manual on your system with info bash):
'${PARAMETER#WORD}'
'${PARAMETER##WORD}'
The WORD is expanded to produce a pattern just as in filename
expansion (*note 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.
#!/bin/bash
tag='123'
version=${tag#?}
echo ${version} # output is: 23
=======
so #? is remove first character?

bash shell reworking variable replace dots by underscore

I can't see to get it working :
echo $VERSIONNUMBER
i get : v0.9.3-beta
VERSIONNUMBERNAME=${VERSIONNUMBER:1}
echo $VERSIONNUMBERNAME
I get : 0.9.3-beta
VERSION=${VERSIONNUMBERNAME/./_}
echo $VERSION
I get : 0_9.3-beta
I want to have : 0_9_3-beta
I've been googling my brains out I can't make heads or tails of it.
Ideally I'd like to remove the v and replace the periods with underscores in one line.
Let's create your variables:
$ VERSIONNUMBER=v0.9.3-beta
$ VERSIONNUMBERNAME=${VERSIONNUMBER:1}
This form only replaces the first occurrence of .:
$ echo "${VERSIONNUMBERNAME/./_}"
0_9.3-beta
To replace all occurrences of ., use:
$ echo "${VERSIONNUMBERNAME//./_}"
0_9_3-beta
Because this approach avoids the creation of pipelines and subshells and the use of external executables, this approach is efficient. This approach is also unicode-safe.
Documentation
From man bash:
${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. 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 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.
(Emphasis added.)
You can combine pattern substitution with tr:
VERSION=$( echo ${VERSIONNUMBER:1} | tr '.' '_' )

Pure bash cutting strings [duplicate]

This question already has answers here:
"${0%/*}" and "${0##*/}" in sh [duplicate]
(2 answers)
Closed 2 years ago.
Please explain the following syntax:
i##*.
i%.*
I understand what it's doing, but I want to know the general pattern (why/how is it doing so).
Code where it appears:
#!/bin/bash
recursive_name_change()
{
cd "$1"
for i in *
do
#echo "${i##*.}"
if [ -d "$i" ]
then
recursive_name_change "$i"
elif [ "${i##*.}" = "cpp" ]
then
new_name=${i%.*}".c"
mv "$i" "$new_name"
fi
done
cd ../
}
recursive_name_change .
Someone please also suggest, where can I find these peculiar syntax forms from?
See Parameter Expansion in man bash:
${parameter#word}
${parameter##word}
Remove matching prefix pattern. 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.
${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 mem-
ber of the array in turn, and the expansion is the resultant list.
In short, # removes the pattern from the left, % from the right, doubling the symbol makes the matching greedy. (Mnemonic: # is to the left of % on most keyboards).
Look for the section "Substring removal" in this manual:
${string##substring}
Deletes longest match of $substring from front of $string.
${string%substring}
Deletes shortest match of $substring from back of $string.

Access * in bash for loop

I have a bash for loop like this:
for i in /long/path/filename*; do
echo ${i%filename*}/other/path/c01/{magic};
done
Now I would like to get {magic} replaced by the text matched by *
Maybe like this:
for i in /long/path/filename*; do
A=${i/\/long\/path\/filename/}
echo ${i%filename*}/other/path/c01/${A}
done
for i in /long/path/filename*;
do echo ${i%filename*}/other/path/c01/${i#/long/path/filename};
done
From man bash:
${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.

ShellScript: Whats the diff between % and %% here?

I am a shell script newbie. I want to know the difference between
${var%pattern}
and
${var%%pattern}
Thanks
From man bash:
${parameter%word}
${parameter%%word}
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.
Here's an example of what the difference is:
$ VAR=abcdefabcdef
$ echo ${VAR%def*}
abcdefabc
$ echo ${VAR%%def*}
abc
Notice that there are two possible matches for def* at the end of $VAR: both "defabcdef" and just "def" match. With the "%" the shortest possible match for the pattern def* is deleted, so the trailing "def" is removed. With the "%%" the longest possible match is deleted, so "defabcdef" bites the dust.
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.

Resources