'How to extract last part of string in bash?
I have this variable:
A="Some variable has value abc.123"
I need to extract this value i.e abc.123
. Is this possible in bash?
Solution 1:[1]
How do you know where the value begins? If it's always the 5th and 6th words, you could use e.g.:
B=$(echo "$A" | cut -d ' ' -f 5-)
This uses the cut
command to slice out part of the line, using a simple space as the word delimiter.
Solution 2:[2]
Simplest is
echo "$A" | awk '{print $NF}'
Edit: explanation of how this works...
awk
breaks the input into different fields, using whitespace as the separator by default. Hardcoding 5
in place of NF
prints out the 5th field in the input:
echo "$A" | awk '{print $5}'
NF
is a built-in awk
variable that gives the total number of fields in the current record. The following returns the number 5 because there are 5 fields in the string "Some variable has value abc.123"
:
echo "$A" | awk '{print NF}'
Combining $
with NF
outputs the last field in the string, no matter how many fields your string contains.
Solution 3:[3]
Yes; this:
A="Some variable has value abc.123"
echo "${A##* }"
will print this:
abc.123
(The ${parameter##word}
notation is explained in ยง3.5.3 "Shell Parameter Expansion" of the Bash Reference Manual.)
Solution 4:[4]
Some examples using parameter expansion
A="Some variable has value abc.123"
echo "${A##* }"
abc.123
Longest match on " " space
echo "${A% *}"
Some variable has value
Longest match on . dot
echo "${A%.*}"
Some variable has value abc
Shortest match on " " space
echo "${A%% *}"
some
Read more Shell-Parameter-Expansion
Solution 5:[5]
The documentation is a bit painful to read, so I've summarised it in a simpler way.
Note that the '*
' needs to swap places with the '
' depending on whether you use #
or %
. (The *
is just a wildcard, so you may need to take off your "regex hat" while reading.)
${A% *}
- remove shortest trailing*
(strip the last word)${A%% *}
- remove longest trailing*
(strip the last words)${A#* }
- remove shortest leading*
(strip the first word)${A##* }
- remove longest leading*
(strip the first words)
Of course a "word" here may contain any character that isn't a literal space.
You might commonly use this syntax to trim filenames:
${A##*/}
removes all containing folders, if any, from the start of the path, e.g./usr/bin/git
->git
/usr/bin/
-> (empty string)${A%/*}
removes the last file/folder/trailing slash, if any, from the end:/usr/bin/git
->/usr/bin
/usr/bin/
->/usr/bin
${A%.*}
removes the last extension, if any (just be wary of things likemy.path/noext
):archive.tar.gz
->archive.tar
Solution 6:[6]
As pointed out by Zedfoxus here. A very clean method that works on all Unix-based systems. Besides, you don't need to know the exact position of the substring.
A="Some variable has value abc.123"
echo "$A" | rev | cut -d ' ' -f 1 | rev
# abc.123
Solution 7:[7]
More ways to do this:
(Run each of these commands in your terminal to test this live.)
For all answers below, start by typing this in your terminal:
A="Some variable has value abc.123"
The array example (#3 below) is a really useful pattern, and depending on what you are trying to do, sometimes the best.
1. with awk
, as the main answer shows
echo "$A" | awk '{print $NF}'
2. with grep
:
echo "$A" | grep -o '[^ ]*$'
- the
-o
says to only retain the matching portion of the string - the
[^ ]
part says "don't match spaces"; ie: "not the space char" - the
*
means: "match 0 or more instances of the preceding match pattern (which is[^ ]
), and the$
means "match the end of the line." So, this matches the last word after the last space through to the end of the line; ie:abc.123
in this case.
3. via regular bash "indexed" arrays and array indexing
Convert A
to an array, with elements being separated by the default IFS
(Internal Field Separator) char, which is space:
- Option 1 (will "break in mysterious ways", as @tripleee put it in a comment here, if the string stored in the
A
variable contains certain special shell characters, so Option 2 below is recommended instead!):# Capture space-separated words as separate elements in array A_array A_array=($A)
- Option 2 [RECOMMENDED!]. Use the
read
command, as I explain in my answer here, and as is recommended by the bashshellcheck
static code analyzer tool for shell scripts, in ShellCheck rule SC2206, here.# Capture space-separated words as separate elements in array A_array, using # a "herestring". # See my answer here: https://stackoverflow.com/a/71575442/4561887 IFS=" " read -r -d '' -a A_array <<< "$A"
Then, print only the last elment in the array:
# Print only the last element via bash array right-hand-side indexing syntax
echo "${A_array[-1]}" # last element only
Output:
abc.123
Going further:
What makes this pattern so useful too is that it allows you to easily do the opposite too!: obtain all words except the last one, like this:
array_len="${#A_array[@]}"
array_len_minus_one=$((array_len - 1))
echo "${A_array[@]:0:$array_len_minus_one}"
Output:
Some variable has value
For more on the ${array[@]:start:length}
array slicing syntax above, see my answer here: Unix & Linux: Bash: slice of positional parameters, and for more info. on the bash "Arithmetic Expansion" syntax, see here:
Solution 8:[8]
You can use a Bash regex:
A="Some variable has value abc.123"
[[ $A =~ [[:blank:]]([^[:blank:]]+)$ ]] && echo "${BASH_REMATCH[1]}" || echo "no match"
Prints:
abc.123
That works with any [:blank:]
delimiter in the current local (Usually [ \t]
). If you want to be more specific:
A="Some variable has value abc.123"
pat='[ ]([^ ]+)$'
[[ $A =~ $pat ]] && echo "${BASH_REMATCH[1]}" || echo "no match"
Solution 9:[9]
echo "Some variable has value abc.123"| perl -nE'say $1 if /(\S+)$/'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | tripleee |
Solution 2 | tripleee |
Solution 3 | ruakh |
Solution 4 | koola |
Solution 5 | |
Solution 6 | tripleee |
Solution 7 | |
Solution 8 | |
Solution 9 | mrqiao001 |