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
.
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 ':'
}
${foo%:*}
. #
- from beginning; %
- from end. #
, %
- shortest match; ##
, %%
- longest match. –
Habitude echo ${pwd##*/}
does not work. –
Jeramie pwd
as a variable. Try dir=$(pwd); echo ${dir##*/}
. Works for me! –
Sheply echo ${$(pwd)##*/}
–
Schmidt :
with /
. –
Sword 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.
echo "1 2 3 4" | rev | cut -d " " -f1 | rev
–
Asylum rev
, was just what I needed! cut -b20- | rev | cut -b10- | rev
–
Sorrow rev
is not safe with multi-byte Unicode characters! Therefore some corner cases might not work with rev
. –
Bead LC_ALL=en_US.utf8
, running echo 'hé' | rev
correctly returns éh
. I have to run echo 'hé' | LC_ALL=C rev
to get an error: rev: stdin: Invalid or incomplete multibyte or wide character
. –
Trevar -sh: rev: command not found
on my NAS, seems like rev is not so common, otherwise I agree it better answers a question about cut –
Quasijudicial cut
, I can turn things like www.google.com into google.com! –
Interval cat domains.txt | rev | cut -d. -f2 | rev | sort | uniq -c | sort -rn
–
Ozonize It's difficult to get the last field using cut, but here are some solutions in awk and perl
echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'
/
character: /a/b/c/d
and /a/b/c/d/
yield the same result (d
) when processing pwd | awk -F/ '{print $NF}'
. The accepted answer results in an empty result in the case of /a/b/c/d/
–
Jalousie /
as delimiter, and if your path is /my/path/dir/
it will use value after last delimiter, which is simply an empty string. So it's best to avoid trailing slash if you need to do such a thing like I do. –
Destructor awk '{$NF=""; print $0}' FS=: OFS=:
often works well enough. –
Hylophagous 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.
You could try something like this if you want to use cut
:
echo "1:2:3:4:5" | cut -d ":" -f5
You can also use grep
try like this :
echo " 1:2:3:4:5" | grep -o '[^:]*$'
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
.
\n
, but for most cases is the most readable solution. –
Hirst 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
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.
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.
a
, not the last field. –
Constancy Using Bash.
$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo \$${#}
0
echo ${!#}
instead of eval echo \$${#}
. –
Signory 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
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.
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
for x in `echo $str | tr ";" "\n"`; do echo $x; done
improving from @mateusz-piotrowski and @user3133260 answer,
echo "a:b:c:d::e:: ::" | tr ':' ' ' | xargs | tr ' ' '\n' | tail -1
first, tr ':' ' ' -> replace ':' with whitespace
then, trim with xargs
after that, tr ' ' '\n' -> replace remained whitespace to newline
lastly, tail -1 -> get the last string
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.
Edit (Dec 2020): Pythonpy is no longer online. Here is an alternative:
$ echo "a:b:c:d:e" | python -c 'import sys; sys.stdout.write(sys.stdin.read().split(":")[-1])'
it contains more boilerplate code (i.e. sys.stdout.read/write
) but requires only std libraries from python.
© 2022 - 2024 — McMap. All rights reserved.
5
if the string is1:2:3:4:5:
(while using the string operators yields an empty result). This is especially handy when parsing paths that could contain (or not) a finishing/
character. – Jalousie