Delete everyting preceding and including a certain substring from variable - bash

In Bash, how can I delete characters from a variable until a certain substring?
Example:
ananas1kiwi2apple1banana2tree
shall look like this:
apple1banana2tree
The substring in this case is 2.

If you want to remove the substring upto 2, using bash parameter expansion:
${var#*2}
# does non-greedy match from left, use ## for greediness
#*2 matches and discards upto first 2 from variable var
Example:
$ var='ananas1kiwi2apple1banana2tree'
$ echo "${var#*2}"
apple1banana2tree

Using pure bash shell parameter expansion.
$ string="ananas1kiwi2apple1banana2tree"
$ newString="${string#*2}"
$ printf "%s\n" "$newString"
apple1banana2tree

Related

How to keep/remove numbers in a variable in shell?

I have a variable such as:
disk=/dev/sda1
I want to extract:
only the non numeric part (i.e. /dev/sda)
only the numeric part (i.e. 1)
I'm gonna use it in a script where I need the disk and the partition number.
How can I do that in shell (bash and zsh mostly)?
I was thinking about using Shell parameters expansions, but couldn't find working patterns in the documentation.
Basically, I tried:
echo ${disk##[:alpha:]}
and
echo ${disk##[:digit:]}
But none worked. Both returned /dev/sda1
With bash and zsh and Parameter Expansion:
disk="/dev/sda12"
echo "${disk//[0-9]/} ${disk//[^0-9]/}"
Output:
/dev/sda 12
The expansions kind-of work the other way round. With [:digit:] you will match only a single digit. You need to match everything up until, or from a digit, so you need to use *.
The following looks ok:
$ echo ${disk%%[0-9]*} ${disk##*[^0-9]}
/dev/sda 1
To use [:digit:] you need double braces, cause the character class is [:class:] and it itself has to be inside [ ]. That's why I prefer 0-9, less typing*. The following is the same as above:
echo ${disk%%[[:digit:]]*} ${disk##*[^[:digit:]]}
* - Theoretically they may be not equal, as [0-9] can be affected by the current locale, so it may be not equal to [0123456789], but to something different.
You have to be careful when using patterns in parameter substitution. These patterns are not regular expressions but pathname expansion patterns, or glob patterns.
The idea is to remove the last number, so you want to make use of Remove matching suffix pattern (${parameter%%word}). Here we remove the longest instance of the matched pattern described by word. Representing single digit numbers is easily done by using the pattern [0-9], however, multi-digit numbers is harder. For this you need to use extended glob expressions:
*(pattern-list): Matches zero or more occurrences of the given patterns
So if you want to remove the last number, you use:
$ shopt -s extglob
$ disk="/dev/sda1"
$ echo "${disk#${disk%%*([0-9])}} "${disk%%*([0-9])}"
1 dev/sda
$ disk="/dev/dsk/c0t2d0s0"
$ echo "${disk#${disk%%*([0-9])}} "${disk%%*([0-9])}"
0 /dev/dsk/c0t2d0s
We have to use ${disk#${disk%%*([0-9])}} to remove the prefix. It essentially searches the last number, removes it, uses the remainder and remove that part again.
You can also make use of pattern substitution (${parameter/pattern/string}) with the anchors % and # to anchor the pattern to the begin or end of the parameter. (see man bash for more information). This is completely equivalent to the previous solution:
$ shopt -s extglob
$ disk="/dev/sda1"
$ echo "${disk/${disk/%*([0-9])}/}" "${disk/%*([0-9])}"
1 dev/sda
$ disk="/dev/dsk/c0t2d0s0"
$ echo "${disk/${disk/%*([0-9])}/}" "${disk/%*([0-9])}"
0 /dev/dsk/c0t2d0s

Bash: remove everything before a certain char [duplicate]

In Bash, how can I delete characters from a variable until a certain substring?
Example:
ananas1kiwi2apple1banana2tree
shall look like this:
apple1banana2tree
The substring in this case is 2.
If you want to remove the substring upto 2, using bash parameter expansion:
${var#*2}
# does non-greedy match from left, use ## for greediness
#*2 matches and discards upto first 2 from variable var
Example:
$ var='ananas1kiwi2apple1banana2tree'
$ echo "${var#*2}"
apple1banana2tree
Using pure bash shell parameter expansion.
$ string="ananas1kiwi2apple1banana2tree"
$ newString="${string#*2}"
$ printf "%s\n" "$newString"
apple1banana2tree

Trim a string (tailing end) based on a specific character in Bash

I was looking to try and figure out how trim a string in Bash, from the trailing end, once I hit a certain character.
Example: if my string is this (or any link): https://www.cnpp.usda.gov/Innovations/DataSource/MyFoodapediaData.zip
(I'll set that as my variable).
(I.e. if I echo $var it will return that link:)
I'm looking to use Bash, I'm guessing I will need to utilize sed or awk, but I want to trim, starting from the end until I see the first / (since the will be the file name) and strip that out.
So using that link, I'm trying to just get after the / so jus "MyFoodapediaData.zip" and set that to a different variable.
So in the end, if I echo $var2 (if I call it that) it will just return: MyFoodapediaData.zip"
I tried working with sed 's.*/" and that will start from the beginning until it finds the first slash. I was looking for the reverse order if possible.
You can use bash builtin parameter substitution for this:
$ var='https://www.cnpp.usda.gov/Innovations/DataSource/MyFoodapediaData.zip'
$ echo "$var"
https://www.cnpp.usda.gov/Innovations/DataSource/MyFoodapediaData.zip
$ var2=${var##*/}
$ echo "$var2"
MyFoodapediaData.zip
${var##*/} means "from the beginning of the value of the var variable, remove everything up to the last slash."
See parameter substitution in the manual

Confusion about bash parameter substitution

I have a script to rename a file or a series of files that contain a specific string
for i in "${#:3}"; do
mv -- "$i" "${i/$1/$2}"
done
so when i do
bash script_name patter_1 pattern_2 string*
it does work but when i try:
mv -- "$i" "${i//$1/$2}"
it still works
${i/$1/$2}
and
${i//$1/$2}
so why is that happening, i search bash guide for beginner but still have no clue. Thank you
From the 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
So if the replacement can be done just once, these are equivalent:
${parameter/pattern/string}
^
${parameter//pattern/string}
^^
See an example:
$ i="hello"
$ echo ${i/e/XX} #just one replacement
hXXllo # <----------------------
$ echo ${i//e/XX} # multiple replacements | they are the same
hXXllo # <----------------------
$ echo ${i/l/XX} #just one replacement
heXXlo # it just happens once!
$ echo ${i//l/XX} #multiple replacements
heXXXXo # it happens many times!

How truncate the ../ characters from string in bash?

How can I truncate the ../ or .. characters from string in bash
So, If I have strings
str1=../lib
str2=/home/user/../dir1/../dir2/../dir3
then how I can get string without any .. characters in a string like after truncated result should be
str1=lib
str2=/home/user/dir1/dir2/dir3
Please note that I am not interesting in absolute path of string.
You don't really need to fork a sub-shell to call sed. Use bash parameter expansion:
echo ${var//..\/}
str1=../lib
str2=/home/user/../dir1/../dir2/../dir3
echo ${str1//..\/} # Outputs lib
echo ${str2//..\/} # Outputs /home/user/dir1/dir2/dir3
You could use:
pax> str3=$(echo $str2 | sed 's?\.\./??g') ; echo $str3
/home/user/dir1/dir2/dir3
Just be aware (as you seem to be) that's a different path to the one you started with.
If you're going to be doing this infrequently, forking an external process to do it is fine. If you want to use it many times per second, such as in a tight loop, the internal bash commands will be quicker:
pax> str3=${str2//..\/} ; echo $str3
/home/user/dir1/dir2/dir3
This uses bash pattern substitution as described in the man page (modified slightly to adapt to the question at hand):
${parameter/pattern/string}
The 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.
If string is null, matches of pattern are deleted and the / following pattern may be omitted.
You can use sed to achieve it
sed 's/\.\.\///g'
For example
echo $str2 | sed 's/\.\.\///g'
OP => /home/user/dir1/dir2/dir3

Resources