使用grep进行正则表达式格式化

时间:2014-04-23 13:16:14

标签: regex bash sed grep

我正在为我的应用程序解析mp3文件的IDv3标签,我正在使用id3v2。例如,要获取mp3文件的专辑名称,命令为

id3v2 -R sample.mp3 | grep "TALB"

输出

TALB: Album Name

但是,我想只得到TALB的值,而不是整行,所以我把它传递给sed,如下所示

id3v2 -R sample.mp3 | grep "TALB" | sed 's/TALB: //'

输出

Album Name

所以,这是我的问题:

  1. 只有grep才能实现上述结果。如果是这样,我该怎么办?
  2. 即使我得到了我想要的结果,但是当我正在寻找其他标签,例如TCON时,它会返回example.com (255),其中我只需要example.com。那么,我如何在这个类型的grep中编写正则表达式?

3 个答案:

答案 0 :(得分:4)

您可以将Perl样式的正则表达式与-P标志一起使用:

grep -Po '(?<=TALB: ).*'

例如:

$ echo "TALB: Album Name" | grep -Po '(?<=TALB: ).*'
Album Name

(?<=TALB: )zero-width look-behind assertion.*是你想要匹配的其他东西的模式 - 在第一种情况下,一切。如果您只想匹配标记字符串后面的特定字符串,就像在第二个问题中那样,那么您可以更改它以适合您的特定数据。

答案 1 :(得分:2)

这是一种更动态的方法,只使用bash而不使用外部,您可以使用它来将所有/所需的标签读取到同名的变量中以供进一步使用。这样更快,为您提供所需的所有标签,无需进一步的成本/代码。

另一个好处是,这是可移植的(<<<需要bash 2.05${foo,,}需要bash 4但可以轻松删除,其余的是POSIX)。 grep -P虽然非常方便,但在OS X上不再可用,因此如果主要平台之间的可移植性受到关注,则不太适合脚本。

#!/bin/bash

# read line-wise from stdin using colon ':' as additional delimiter.
# what comes before ':' gets stored in $key, what comes after in $value
while IFS=' \t:' read -r key value; do
    # check if the value of $key is a tag that we are interested in
    case ${key} in
        TALB|TCON|TFOO) ;; # $key is one of TALB, TCON or TFOO. let's continue
        *) continue     ;; # otherwise, ignore line and read next line
    esac

    # store $value in the variable named $key, e.g. if
    #   $key == "TCON" and $value == "Album Name"
    # this would create $tcon == "Album Name"
    # (${key,,} converts $key to lowercase during the process)
    read -r "${key,,}" <<< "${value}"
done <<__DATA__
TALB: Album Name
TCON: example.com (255)
TFOO: tfoo
TBAR: tbar
__DATA__

echo "TALB: $talb"
echo "TCON: ${tcon% (255)}"  # strip the trailing " (255)" from $tcon
echo "TFOO: $tfoo"
echo "TBAR: $tbar"

$ ./t.sh
TALB: Album Name
TCON: example.com
TFOO: tfoo
TBAR:

${foo,,} bash 4概念是将$foo扩展为小写以避免使用大写变量名称,但如果您没有bash 4,则可以放弃此使用大写名称或使用tr将它们转换为小写。

while read line - 成语很好地解释了here

而不是我用来模拟id3v2输出的heredoc,而是使用process substitution来将id3v2的输出传输到循环中#&#34} 34;,例如

while [...]; do
    [...]
done < <(id3v2 -R Idhayam.mp3)

答案 2 :(得分:1)

我认为awk这是一个很好的工作:

id3v2 -R Idhayam.mp3 | awk -F': ' '/^TALB/ {print $2}'

将在冒号后打印部件。 -F开关用于指定分隔符,在本例中为分隔符,后跟空格。如果在行的开头找到TALB,则打印第二列。

“TALB”部分可以放入变量中,例如:

id3v2 -R Idhayam.mp3 | awk -F': ' -v i="TALB" '"/^"i"/" {print $2}'

如果您只想要括号前面的部分:

awk -F': ' -v i="TCON" '"/^"i"/" { split($2,a,"("); print a[1] }'

这使用split创建一个数组a,其中包含第二列的内容,(作为分隔符。然后它打印该数组的第一个元素。


如果您更喜欢使用sed,那么也不需要grep。你可以使用这样的正则表达式:

sed -n 's/^TCON: \([^(]*\).*/\1/p'

这仅打印以“TCON”开头的行的相关部分((之前的部分)。将-n传递给sed表示默认情况下不会打印每一行。

  • ^TCON匹配以“TCON:”
  • 开头的行
  • \( \)捕获这些括号之间的东西(需要斜杠作为转义字符)
  • [^(]个字符不是“(”
  • *零或多个
  • .*吞下了剩下的一行

整行代替\1,它指的是之前捕获的部分。 p表示打印。