【问题标题】:Save part of matching pattern to variable将匹配模式的一部分保存到变量
【发布时间】:2011-02-07 03:12:18
【问题描述】:

我想提取与模式匹配的子字符串并将其保存到文件中。示例字符串:

Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk

我想提取括号之间的部分,在本例中为[sdf]

我尝试执行grep -e '[$subtext]' 之类的操作来将括号中的文本保存到变量中。当然它不起作用,但我正在寻找与此类似的方法。在这样的正则表达式中包含一个变量会非常优雅。我最擅长的是什么?

谢谢!

【问题讨论】:

  • 我认为捕获应该括号之间,即。不包括在内?

标签: regex bash variables extraction


【解决方案1】:

可能有更好的方法只使用 bash,但是:

echo 'Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk' \
| sed -s 's/.*\[\(.*\)\].*/\1/'

正如 Jurgen 指出的那样,这匹配不匹配的行。如果您不想输出不匹配的行,请使用 '-n' 使其不输出模式,并使用 '/p' 以在匹配时输出模式。

| sed -n 's/.*\[\(.*\)\].*/\1/p'

【讨论】:

  • 这也会打印不匹配的行
  • OS X 用户需要删除 -s 选项,因为它不受支持。
  • @Stephen 我有类似的问题echo '\title{\hypertarget{A64L}{A64L(3)}}' | sed -e s/\\\\section\{/\\\\section\*{/g -e /\\\\title\{\\\\hypertarget\{\(.*\)\}\{.*\(3\)\}\}/a\\\\\\addcontentsline{toc}{section}{\1} 我希望结果为\title{\hypertarget{A64L}{A64L(3)}} \addcontents{toc}{section}{A64L}
【解决方案2】:

匹配正则表达式,使用分组替换并且仅在正则表达式匹配时打印:

sed -n "s/.*\[\(.*\)\].*/\1/p"

【讨论】:

    【解决方案3】:

    sed 是贪婪的,因此如果您的数据中有更多 [] 对,sed 的答案将丢失一些数据。使用 grep+tr 解决方案,也可以使用 awk

    $ cat file
    [sss]Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk [tag] blah blah
    
    $ awk -F"[" '{for(i=2;i<=NF;i++){if($i~/\]/){sub("].*","",$i)};print $i}}' file
    sss
    sdf
    tag
    

    【讨论】:

      【解决方案4】:

      BASH_REMATCH 是一个数组,其中包含由 shell 匹配的组。

      $ line='Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk'
      $ [[ $line =~ \[([^]]+)\] ]]; echo "${BASH_REMATCH[1]}"
      sdf
      

      如果你想把它放在一个循环中,你可以这样做;这是一个例子:

      while read -r line; do
        if [[ $line =~ \[([^]]+)\] ]] ; then
          drive="${BASH_REMATCH[1]}"
          do_something_with "$drive"
        fi
      done < <(dmesg | egrep '\[([hsv]d[^]]+)\]')
      

      这种方法不会将外部调用放入循环中——因此外壳不需要forkexec 来启动外部程序,例如sedgrep。因此,它可以说比这里提供的其他方法要干净得多。

      顺便说一句,您最初的方法(使用 grep)并没有那么遥远;使用grep -o 将只输出匹配的子字符串:

      $ subtext=$(egrep -o "\[[^]]*\]" <<<"$line")
      

      ...虽然这包括捕获中的括号,因此不是 100% 正确的。

      【讨论】:

      • 但是与 awk (等)相比,bash 的 while 读取循环在迭代大文件时要慢得多。顺便说一句,如果没有 while 循环,我的第一个版本没有输出。不应转义字符范围内的 ]
      • @ghostdog - 已更新,谢谢。我确实按原样获得输出,但那是 bash 4。我同意读取循环很慢——在输入端过滤一次比在循环内过滤要好得多,而且你如果要匹配多行,必须有一个循环。
      • @CharlesDuffy 我猜你可以回答unix.stackexchange.com/a/413680/176227 需要类似的解决方案。只需更正正则表达式即可。
      猜你喜欢
      • 2018-04-07
      • 2022-11-03
      • 1970-01-01
      • 1970-01-01
      • 2021-06-16
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多