【问题标题】:Extract string between two characters in bash提取bash中两个字符之间的字符串
【发布时间】:2012-07-07 20:45:11
【问题描述】:

我有一个格式如下的字符串

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#36/37

我做的第一件事是使用

从第二行提取#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 and awk 只是为了从#36/37 中提取文本不过是矫枉过正。那么,有没有更好或更短的方法来实现这一点。

【问题讨论】:

  • 你的输入真的是一个字符串,还是一个流?
  • @kojiro:基本上它是一个流。你可以看到它是mpc status 命令的输出,但我想知道它有什么不同。

标签: string bash sed awk


【解决方案1】:

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

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

输出:

36

【讨论】:

    【解决方案2】:

    将磅和斜线字符上的字段拆分为一个数组并检索所需的元素。

    awk 'NR==2 {split($2, arr, "[#/]"); print arr[2]}'
    

    【讨论】:

    • 谢谢,我发现您的解决方案与我的很接近,但您能否向我解释 [#/] 正在做什么,或者如果您能指出我的文档会更好。
    • @Noob:参见“字符类和括号表达式”部分中的man grep
    【解决方案3】:

    这可能对你有用:

    sed 's/.*#\([0-9]*\)\/[0-9]*.*/\1/p;d' file
    36
    

    【讨论】:

    • 虽然它会选择任何专辑或歌曲标题中恰好有类似 #1/2 的东西。甚至是#/,尽管这种情况很少见。
    【解决方案4】:
    input | while read playing numbers rest
    do
      if [[ $playing = "[playing]" ]]; then
        t="${numbers:1}"
        echo "${t%/*}"
      fi
    done
    

    Bash 默认拆分是按空格,所以你在第二个字段(数字)中得到的就是那个数字。剩下的就是使用 bash 参数扩展运算符来获取感兴趣的部分:删除第一个字符并删除以“/”开头的后缀

    【讨论】:

    • 这似乎会输出 'ff'、'36' 和 '/a'
    • 总有另一位test "$playing" = '[playing]' || continue
    • 而且它只使用 shell 内置函数,这可能比外部程序的 fork 和 exec 更快、更便宜。当然,取决于输入的数量。我们很幸运能有这么简单的输入
    • 我仍然认为您的答案本身应该是完整的,或者至少解释一下它缺少什么。即使在编辑之后,这个答案也会输出“ff”和“36”,可能还有一些空行。
    • @fork0 如果不先尝试代码,我不会发表评论。 dpaste.com/768337
    【解决方案5】:
    sed -n '2s/.*\#\([0-9]*\)\/.*/\1/p'
    

    这会抑制除第二行之外的所有内容,然后回显 #/ 之间的数字

    【讨论】:

      【解决方案6】:

      此答案利用 bash 的内置扩展正则表达式语法,使用 =~ 测试运算符。 (我说test,但不要指望它可以与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 数组的第零个元素中。

      【讨论】:

      • 哇,一个奇特的解决方案。我很想了解它是如何工作的。
      • @Noob:更新了解释。 (但也许现在更清楚为什么输入是真正的字符串还是流很重要。在流上使用这种语法会有点麻烦。)
      • 该死,我完全忘记了 bash 正则表达式!谢谢提醒:)
      【解决方案7】:

      这将解决您的问题。

      awk -F'[#/]' 'NR==2{print $2}'
      

      【讨论】:

        【解决方案8】:

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

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

        你可以在GitHub下载这个脚本。

        【讨论】:

          猜你喜欢
          • 2020-10-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-28
          • 1970-01-01
          相关资源
          最近更新 更多