在bash中提取两个字符之间的字符串

6
我有一个如下格式的字符串
Walk Off the Earth - Somebody That I Used to Know
[playing] #36/37   1:04/4:05 (26%)
volume: n/a   repeat: off   random: on    single: off   consume: off

现在,从上面的字符串中,我需要从#36/37中提取出36

我首先要做的是使用

从第二行中提取#36/37
echo "above mentioned string" | awk 'NR==2 {print $2}'

现在,我想从上面提取的部分中提取36,我这样做了。
echo `#36/37` | sed -e 's/\//#/g' | awk -F "#" '{print $2}'

执行后我的输出是36

不过,我认为仅仅使用 sed 和 awk#36/37 中提取文本有些过于复杂了。那么,有没有更好、更简短的方法来实现这个目标呢?


你的输入是字符串还是流? - kojiro
@kojiro:基本上它是一个流。你可以看到它是mpc status命令的输出,但我想知道它有什么区别。 - RanRag
9个回答

6
将字段按井号和斜杠字符拆分为数组,并检索所需元素。
awk 'NR==2 {split($2, arr, "[#/]"); print arr[2]}'

谢谢,我发现你的解决方案接近于我的,但是你能解释一下[#/]是什么意思吗?或者更好的办法是能否指引我去查看文档。 - RanRag
@Noob:请参考“字符类和括号表达式”章节中的man grep - Dennis Williamson

4

这个答案利用了bash内置的扩展正则表达式语法,使用=~测试运算符。(我说“测试”,但不要指望它能与test命令一起使用。它只能与关键字[[一起使用。)

mini:~ michael$ cat foo
Walk Off the Earth - Somebody That I Used to Know
[playing] #36/37   1:04/4:05 (26%)
volume: n/a   repeat: off   random: on    single: off   consume: off

mini:~ michael$ [[ $(<foo) =~ \#[[:digit:]]{2} ]] && echo "${BASH_REMATCH[0]#\#}"
36

简单来说,这只是一个正则表达式,用于匹配井号后面的两个数字,并将它们保存在BASH_REMATCH数组的零号元素中。


哇,这个解决方案太棒了。我很想了解它是如何工作的。 - RanRag
1
@Noob:已更新并解释了。但是现在或许更清楚为什么输入是真实字符串还是流很重要了。在流上使用这种语法会有点繁琐。 - kojiro
该死,我完全忘记了Bash正则表达式!谢谢你提醒 :) - fork0

3

使用 sed 的一种方法,假设 infile 包含问题的内容。在第二行匹配任意字符直到 #,然后保存组 1 中的任何数字,并用该组 \1 替换整行。-n 开关避免打印除代码中指定的 p 指令之外的任何内容。

sed -ne '2 { s/^[^#]*#\([0-9]*\).*$/\1/; p; q }' infile

输出:

36

3
这可能适合你:
sed 's/.*#\([0-9]*\)\/[0-9]*.*/\1/p;d' file
36

尽管它会捕捉到任何包含#1/2的专辑或歌曲标题,甚至是#/,但这种情况很少见。 - Rob Davis

3
input | while read playing numbers rest
do
  if [[ $playing = "[playing]" ]]; then
    t="${numbers:1}"
    echo "${t%/*}"
  fi
done

Bash默认使用空格进行分割,所以第二个字段(数字)就是这些数字。其余内容是使用Bash参数扩展运算符来获取感兴趣的部分:删除第一个字符并删除起始于“/”的后缀。

这似乎会输出 'ff'、'36' 和 '/a'。 - kojiro
1
总有一个地方可以放置另一个 test "$playing" = '[playing]' || continue - fork0
它仅使用shell内置命令,这可能比外部程序的fork和exec更快、更便宜。当然,这取决于输入的数量。我们很幸运拥有这么简单的输入。 - fork0
@fork0 如果没有先尝试代码,我是不会发表评论的。http://dpaste.com/768337/ - kojiro
@fork0 在注释里写并不足够。我的意思是你需要编辑你的回答以包含那部分内容。所以我已经替你编辑了。 - kojiro
显示剩余2条评论

3
sed -n '2s/.*\#\([0-9]*\)\/.*/\1/p'

这将抑制除第二行外的所有内容,然后输出在 #/ 之间的数字。

3
这将解决您的问题。
awk -F'[#/]' 'NR==2{print $2}'

1

使用BASH内置的字符串操作,您可以在不需要任何外部程序的情况下完成此操作,如下所示:

string="[playing] #36/37   1:04/4:05 (26%)"
part=${string##*#};number=${part%%/*}
echo "$number"

1

我编写了一个脚本,可以输出第一个和最后一个字符之间的字符串。为了解决你的问题,你可以使用以下命令与此脚本结合使用。

echo '[playing] #36/37   1:044:05 (26%)' | cut -d' ' -f2 | ./cut_between.sh -f '#' -l '/'

您可以在GitHub上下载此脚本。

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