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

Bash script to extract information from a block of text spanning multiple lines

我正在尝试使用 bash 脚本中的 mkvinfoMKV 文件中提取曲目信息。输出是一长串带有重复模式的行,作为各种轨道类型的各种轨道属性的分隔符。曲目示例是:

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

底线问题是:

How can I extract specific properties of specific track types, such as the example given, and put them into an array or arrays for further processing?

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

  1. 我想使用现有的 bash(GNU bash,版本 4.3.30(1)-release (x86_64-apple-darwin12.5.0))实用程序,例如 sedawkgrep、……
  2. 我不想创建 'intermediate file'
  3. 我想简单地管道 mkvinfo 的输出到各种实用程序

我找到了很多展示如何使用 sed 在两个 单词 之间查找文本块的帖子,但我无法获得与 一起使用的代码=60=]整行或包含空格的字符串。也许有办法做到这一点,但我对 sed 了解不够,无法根据我的情况调整代码。

请详细解释你的代码是如何工作的所以我可以'learn how to fish'所以下次我可以自己做。

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

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

mkvinfo ... | gawk '
    match([=10=], /Track number: ([0-9]+)/, m) {TN=m[1]}
    match([=10=], /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 具有匹配功能来捕获括号内的组。