Bash脚本从跨越多行的文本块中提取信息

时间:2015-04-26 18:36:46

标签: bash awk sed grep

我正尝试使用MKV脚本中的mkvinfobash文件中提取曲目信息。输出是一系列长行,具有重复模式作为各种轨道类型的各种轨道属性的分隔符。轨道的一个例子是:

…
| + A track
|  + Track number: 6 (track ID for mkvmerge & mkvextract: 5)
|  + Track UID: 11555278830806058806
|  + Track type: subtitles
|  + (Unknown element: TrickTrackFlag; ID: 0xc6 size: 3)
|  + Enabled: 1
|  + Default flag: 0
|  + Forced flag: 0
|  + Lacing flag: 0
|  + MinCache: 0
|  + Timecode scale: 1
|  + Name: Spanish
|  + Language: spa
|  + Codec ID: S_TEXT/UTF8
|  + (Unknown element: TrackAttachmentLink; ID: 0x7446 size: 11)
|  + Codec decode all: 1
| + A track
|  + Track number: 7 (track ID for mkvmerge & mkvextract: 6)
…

可以有给定轨道类型的多个实例,并且轨道的行数有些变化。我需要从特定的轨道类型中提取某些轨道属性。例如,如果我想查找subtitles跟踪类型的所有实例并提取Track numberCodec ID,我可以通过grep管道输出结果:

mkvinfo "file.mkv" | grep "subtitles" -B 2 | grep "Track number"

这将输出包含所有字幕轨道的轨道号的行。我必须将这些行放入一个数组并过滤它们以得到第一个数字,这样我就可以将它与mkvpropedit一起使用,这需要第一个数字。

类似地:

mkvinfo "file.mkv" | grep "subtitles" -A 10 | grep "Codec ID: " | sed 's/^.**: //'

输出所有字幕轨道的编解码器ID。

这很好 IF 我确切地知道在包含subtitles的行之前/之后有多少行。问题是,要包含的确切行数因文件而异。所以我需要做的是在| + A track和以|+ OR | + OR EOF开头的行之间输出整行。我还需要过滤块以提取第一个Track numberCodec ID。我尝试使用| grep -Eo [0-9]+ | head -1来提取每首曲目的第一个数字,但它只适用于找到的第一首曲目并退出。如果有一种方法可以让它适用于一行中的所有曲目,那将会很有帮助。我使用sed给出的第二个示例适用于Codec ID

底线问题是:

  

如何提取特定跟踪类型的特定属性 ,例如给出的示例,并将它们放入一个或多个数组中以便进一步处理?

我希望能够满足以下条件:

  1. 我想使用现有的bash(GNU bash,版本4.3.30(1)-release(x86_64-apple-darwin12.5.0))实用程序,如sedawkgrep,...
  2. 我不想创建一个'中间文件'
  3. 我想简单地管道 mkvinfo的输出到各种实用程序中
  4. 我发现很多帖子都显示了如何使用sed在两个单词之间找到一个文本块但是我无法使用整行< / strong>或包含空格的字符串。也许有一种方法可以做到这一点,但我不太了解sed能够使代码适应我的情况。

    请详细说明您的代码如何运作,以便我可以“学习如何钓鱼”。所以下次我可以自己做。

1 个答案:

答案 0 :(得分:2)

当以复杂的方式处理多行时,我选择的工具是awk

在每个匹配模式中,我们将匹配保存在变量中。 最后,当我们遇到指示新块(| + A track)的字符串,或者我们到达流的末尾时,我们打印我们感兴趣的变量的值(轨道号,编解码器ID),但仅限于类型是字幕。

mkvinfo ... | gawk '
    match($0, /Track number: ([0-9]+)/, m) {TN=m[1]}
    match($0, /Codec ID: (.*)$/, m)        {CI=m[1]}
    /Track type: subtitles/                {SUB=1}
    /^\| \+ A track$/ {if(SUB) print TN, CI; unset SUB}
    END               {if(SUB) print TN, CI; unset SUB}'

您需要gawk来使用匹配功能来捕获带括号的组。