如何在bash中将字符串按最后一个“-”分割?

3
给定字符串"foo-bar-1.4.5""for-bar-baz-1.8.3",如何将这些字符串分成两部分:第一部分是最后一个"-"之前的所有内容("foo-bar""foo-bar-baz"),第二部分是最后一部分("1.4.5""1.8.3")?我可以想象几种策略,比如在"-"上分割,然后将除了最后一部分以外的所有内容重新组合起来。或者使用正则表达式进行替换。不过,Bash是我遇到的最大障碍。我不确定语法应该是什么样子的,我已经尝试了一些用sedawk的方法,但是我对Bash数组的不熟悉使我受阻了。我觉得你们中的一位Bash专家可以通过一个例子很快地帮我解决这个问题并教我怎么做。注意:在运行命令之前,我无法知道"-"的数量。这种方法应该能够处理n >= 1个"-"符号。

(换句话说,你对于除了基本的bash之外的编程语言也很熟悉吗?) - Michael Berkowski
@MichaelBerkowsk 我只安装了新的debian:jessie包。所以perl不行。但是sedawk可以。 - Dane O'Connor
Bash本身可以完成许多任务。 - Mat
1
(简单来说,echo "foo-bar-bax-1.8.3" | awk -F'-' '{print $(NF-1)}' 或者类似的东西) - njzk2
@njzk2 我不同意重复建议。即使用于解决问题的工具相同,目标也是不同的。不过你的方法看起来很有趣!我之前不知道 NF。 - Dane O'Connor
显示剩余4条评论
3个回答

7
你可以使用参数扩展来完成这个操作: ${var#pattern}会将变量var开头最短匹配的pattern移除。使用##则是移除最长匹配的pattern。%和%%的用法类似,但是是从变量结尾处进行移除。
#!/bin/bash
first="foo-bar-1.4.5"
second="foo-bar-baz-1.8.3"

echo ${first%-*}
echo ${first##*-}

echo ${second%-*}
echo ${second##*-}

输出:

foo-bar
1.4.5
foo-bar-baz
1.8.3

在运行命令之前,我不知道有多少个“-”。这似乎必须硬编码,对吗? - Dane O'Connor
不,我会在答案中澄清。 - Josh Jolly
@Mat,也许我还没有理解,但是看到a%%b - Dane O'Connor
@thedeeno:测试变量为stringastringb,其中的a和b不是魔法的一部分。魔法分别从##开始。我在对你的问题的评论中发布的链接有更多细节(和变化)。 - Mat
@Mat 感谢您的清晰解释。我的思维错误地解析了变量名称和模式。 - Dane O'Connor
喜欢这个方法。非常简洁! - Dane O'Connor

2

要在bash中使用正则表达式:

s="foo-bar-1.4.5"
[[ $s =~ (.*)-(.*) ]]
name=${BASH_REMATCH[1]}
version=${BASH_REMATCH[2]}

bash 只使用贪婪匹配,所以第一个 .* 将尽可能匹配,只留下最后一个 - 与正则表达式中的字面量 - 匹配。


1

使用bash内置功能是正确的方法。但是,作为参考,这里提供了一种使用awk的选项:

$ cat file
foo-bar-1.4.5
for-bar-baz-1.8.3

$ awk 'BEGIN{FS=OFS="-"}{last = $NF; NF--; printf "First = %s\nLast = %s\n", $0, last}' file
First = foo-bar
Last = 1.4.5
First = for-bar-baz
Last = 1.8.3


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接