12

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

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

我想提取括号之间的部分,在这种情况下[sdf]

我试图做一些事情,比如grep -e '[$subtext]'将括号中的文本保存到变量中。当然它不起作用,但我正在寻找与此类似的方法。在这样的正则表达式中包含一个变量会非常优雅。我能做什么最好?

谢谢!

4

4 回答 4

13

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[^]]+)\]')

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

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

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

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

于 2010-04-13T00:49:53.433 回答
10

仅使用 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'
于 2010-04-12T18:23:50.187 回答
4

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

sed -n "s/.*\[\(.*\)\].*/\1/p"
于 2010-04-12T19:31:58.507 回答
1

[]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
于 2010-04-13T00:30:29.193 回答