Question

Suppose I have the string 1:2:3:4:5 and I want to get its last field (5 in this case). How do I do that using Bash? I tried cut, but I don't know how to specify the last field with -f.

Was it helpful?

Solution

You can use string operators:

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

This trims everything from the front until a ':', greedily.

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

OTHER TIPS

Another way is to reverse before and after cut:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

This makes it very easy to get the last but one field, or any range of fields numbered from the end.

It's difficult to get the last field using cut, but here's (one set of) solutions in awk and perl

$ echo 1:2:3:4:5 | awk -F: '{print $NF}'
5
$ echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'
5

Assuming fairly simple usage (no escaping of the delimiter, for example), you can use grep:

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

Breakdown - find all the characters not the delimiter ([^:]) at the end of the line ($). -o only prints the matching part.

One way:

var1="1:2:3:4:5"
var2=${var1##*:}

Another, using an array:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}

Yet another with an array:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}

Using Bash (version >= 3.2) regular expressions:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Simply translate the delimiter into a newline and choose the last entry with tail -1.

Using sed:

$ echo '1:2:3:4:5' | sed 's/.*://' # => 5

$ echo '' | sed 's/.*://' # => (empty)

$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c

$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c

If your last field is a single character, you could do this:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

Check string manipulation in bash.

There are many good answers here, but still I want to share this one using basename :

 basename $(echo "a:b:c:d:e" | tr ':' '/')

However it will fail if there are already some '/' in your string. If slash / is your delimiter then you just have to (and should) use basename.

It's not the best answer but it just shows how you can be creative using bash commands.

Using Bash.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

First use xargs split it using ":",-n1 means every line only have one part.Then,pring the last part.

for x in `echo $str | tr ";" "\n"`; do echo $x; done

For those that comfortable with Python, https://github.com/Russell91/pythonpy is a nice choice to solve this problem.

$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'

From the pythonpy help: -x treat each row of stdin as x.

With that tool, it is easy to write python code that gets applied to the input.

Might be a little late with the answer though a simple solution is to reverse the ordering of the input string. This would allow you to always gain the last item no matter the length.

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

It is important to note though, if using this method and the numbers are larger than 1 digit (Or larger than one character in any circumstance), you will need to run another 'rev' command over the piped output.

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

Hope I can help, cheers

A solution using the read builtin:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

Or, to make it more generic:

echo "${fields[-1]}" # prints the last item

If you like python and have an option to install a package, you can use this python utility.

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

Regex matching in sed is greedy (always goes to the last occurrence), which you can use to your advantage here:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top