如何在shell中拆分字符串并获取最后一个字段

Posted

技术标签:

【中文标题】如何在shell中拆分字符串并获取最后一个字段【英文标题】:How to split a string in shell and get the last field 【发布时间】:2011-03-10 21:29:31 【问题描述】:

假设我有字符串1:2:3:4:5,我想获取它的最后一个字段(在本例中为5)。我如何使用 Bash 做到这一点?我试过cut,但我不知道如何用-f指定最后一个字段。

【问题讨论】:

【参考方案1】:

你可以使用string operators:

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

这会贪婪地修剪从前面到 ':' 的所有内容。

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

【讨论】:

虽然这适用于给定的问题,但如果字符串为 1:2:3:4:5:,则以下 William 的答案 (***.com/a/3163857/520162) 也会返回 5(使用字符串运算符时会产生空结果) .这在解析可能包含(或不包含)结束 / 字符的路径时特别方便。 你会如何做相反的事情?回显'1:2:3:4:'? 如何保留最后一个分隔符之前的部分?显然是通过使用$foo%:*# - 从头开始​​; % - 从头到尾。 #, % - 最短匹配; ##, %% - 最长匹配。 如果我想从路径中获取最后一个元素,我应该如何使用它? echo $pwd##*/ 不起作用。 @Putnik 该命令将pwd 视为变量。试试dir=$(pwd); echo $dir##*/。为我工作!【参考方案2】:

另一种方法是在cut前后反转:

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

这使得获取最后一个字段或从末尾编号的任何字段范围变得非常容易。

【讨论】:

这个答案很好,因为它使用了“cut”,作者(大概)已经很熟悉了。另外,我喜欢这个答案,因为 正在使用“cut”并且有这个确切的问题,因此通过搜索找到了这个线程。 一些使用空格作为分隔符的剪切和粘贴素材:echo "1 2 3 4" | rev | cut -d " " -f1 | rev 版本 |切-d -f1 | rev 太聪明了!谢谢!帮了我一堆(我的用例是 rev | -d ' ' -f 2- | rev 我总是忘记rev,这正是我所需要的! cut -b20- | rev | cut -b10- | rev 我最终得到了这个解决方案,我尝试用“awk -F”/“'print $NF'”剪切文件路径对我来说有点失败,因为包含空格的文件名也得到了切开【参考方案3】:

使用cut很难得到最后一个字段,但是这里有一些在awk和perl中的解决方案

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

【讨论】:

这个解决方案相对于公认的答案的巨大优势:它还匹配包含或不包含结束 / 字符的路径:/a/b/c/d/a/b/c/d/ 产生相同的结果 (d ) 处理pwd | awk -F/ 'print $NF' 时。在/a/b/c/d/的情况下,接受的答案会导致空结果 @eckes 在 AWK 解决方案的情况下,在 GNU bash 版本 4.3.48(1)-release 上,这是不正确的,因为无论是否有斜杠,这都很重要。简单地说,AWK 将使用/ 作为分隔符,如果您的路径是/my/path/dir/,它将使用最后一个分隔符之后的值,这只是一个空字符串。所以如果你需要像我一样做这样的事情,最好避免使用斜杠。 如何获得子字符串 UNTIL 最后一个字段? @blackjacx 有一些怪癖,但像awk '$NF=""; print $0' FS=: OFS=: 这样的东西通常效果很好。【参考方案4】:

假设使用相当简单(例如,没有转义分隔符),您可以使用 grep:

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

Breakdown - 在行尾 ($) 查找除定界符 ([^:]) 之外的所有字符。 -o 只打印匹配的部分。

【讨论】:

-E 表示使用扩展语法; [^...] 表示除列出的字符之外的任何内容; + 一个或多个这样的命中(将采用模式的最大可能长度;此项是 gnu 扩展) - 例如,分隔字符是冒号。【参考方案5】:

一种方式:

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

另一个,使用数组:

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

还有一个数组:

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

使用 Bash(版本 >= 3.2)正则表达式:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=$BASH_REMATCH[1]

【讨论】:

【参考方案6】:

如果你想使用cut,你可以试试这样的:

echo "1:2:3:4:5" | cut -d ":" -f5

你也可以使用grep试试这样:

echo " 1:2:3:4:5" | grep -o '[^:]*$'

【讨论】:

您的第二个命令对我很有用。你能把它分解一下,以便我能更好地理解它吗?谢谢。【参考方案7】:
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

只需将分隔符转换为换行符,然后使用tail -1 选择最后一个条目。

【讨论】:

如果最后一项包含\n,它将失败,但在大多数情况下是最易读的解决方案。【参考方案8】:

使用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

【讨论】:

鉴于许多实用程序的输出是原始文件名后跟冒号 (:) 后跟实用程序输出 ($path:$output) 的形式,这非常有用用于在最后一个冒号之后添加您自己的控制字符,如 TAB $'\t' 或单位分隔符 $'\037' 等。在文件输出的最后一个冒号处添加 TAB 的示例:file ~/yourPath/* | sed "s/\(.*:\)\(.*\)/\1"$'\t'"\2/"【参考方案9】:

如果您的最后一个字段是单个字符,您可以这样做:

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

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

检查string manipulation in bash。

【讨论】:

这不起作用:它给出了a 的最后一个字符,而不是最后一个字段 没错,就是这样,如果您知道最后一个字段的长度,那就太好了。如果不是,你必须使用其他东西......【参考方案10】:

这里有很多好的答案,但我还是想用 basename 分享这个:

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

但是,如果您的字符串中已经有一些“/”,它会失败。 如果斜杠 / 是您的分隔符,那么您只需要(并且应该)使用 basename。

这不是最佳答案,但它只是展示了如何使用 bash 命令发挥创造力。

【讨论】:

【参考方案11】:

使用 Bash。

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$$#
0

【讨论】:

可以使用echo $!# 而不是eval echo \$$#【参考方案12】:
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

先用xargs用“:”分割,-n1表示每一行只有一个部分。然后,pring最后一部分。

【讨论】:

【参考方案13】:

使用 read 内置的解决方案:

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

或者,让它更通用:

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

【讨论】:

【参考方案14】:
for x in `echo $str | tr ";" "\n"`; do echo $x; done

【讨论】:

如果任何字段中有空格,就会出现问题。此外,它没有直接解决检索 last 字段的问题。【参考方案15】:

sed 中的正则表达式匹配是贪婪的(总是到最后一次出现),您可以在这里使用它来发挥自己的优势:

$ foo=1:2:3:4:5
$ echo $foo | sed "s/.*://"
5

【讨论】:

【参考方案16】:

对于那些熟悉 Python 的人来说,https://github.com/Russell91/pythonpy 是解决这个问题的不错选择。

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

来自 pythonpy 帮助:-x treat each row of stdin as x

使用该工具,可以轻松编写应用于输入的 Python 代码。

编辑(2020 年 12 月): Pythonpy 不再在线。 这是一个替代方案:

$ echo "a:b:c:d:e" | python -c 'import sys; sys.stdout.write(sys.stdin.read().split(":")[-1])'

它包含更多样板代码(即sys.stdout.read/write),但只需要来自 python 的标准库。

【讨论】:

【参考方案17】:

如果你喜欢 python 并且可以选择安装包,你可以使用this python utility。

# install pythonp
pythonp -m pip install pythonp

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

【讨论】:

python可以直接这样做:echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])" @MortenB 你错了。 pythonp 包的全部目的是让你做与 python -c 相同的事情,但字符输入更少。请查看存储库中的 README。

以上是关于如何在shell中拆分字符串并获取最后一个字段的主要内容,如果未能解决你的问题,请参考以下文章

如何避免作为 sql 查询输出的一部分返回的字符串值被拆分为 bash/shell 脚本中数组中的不同字段

获取拆分字符串数组的最后一个元素

如何在点上拆分字符串并有效地提取所有字段?

在字段中拆分字符串并用作 sql 中的新列名

在 C++ 中拆分点上的字符串并从中提取所有字段?

linux shell脚本:拆分字符串,将它们放在一个数组中然后循环它们[重复]