从多行中提取数据字段

从多行中提取数据字段

我经常发现自己需要将文本从一种格式转换为另一种格式。一个典型的情况是我有一个包含一些信息的日志文件,我想找出该信息的子集,并以非常特定的方式格式化。


现在,如果每个“记录”都在同一行,则任务相对容易:

  • 可以选择用于grep仅匹配感兴趣的行。
  • 用于sed匹配感兴趣的字段,捕获它们,并输出按要求格式化的结果。

唯一的小问题是需要创建一个与全部的行,因此sed将丢弃它并仅输出所需的输出。但这并不太难。

如果场捕获问题足够简单,cut则可能就足够了,或者也许awk


现在,真正的问题是:如果我想要的字段跨越多行怎么办?

举例来说,假设有人

stat * >LOG.TXT

好吧,现在我们有了一个LOG.TXT包含我们想要的所有信息的文件......但它看起来像

  File: 'src.7z'
  Size: 269430          Blocks: 528        IO Block: 4096   regular file
Device: 801h/2049d      Inode: 799155      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-07-02 09:01:09.269292914 +0100
Modify: 2020-07-02 09:00:53.237293629 +0100
Change: 2020-07-02 09:00:53.237293629 +0100
 Birth: -

也许我们想要生成一些看起来更像

VerifyFile("src.7z", 269430, 0644);

现在,正确的方法要做到这一点,首先就是要求stat以这种方式输出!一般来说,它是总是最好让源程序生成您想要开始的格式,而不是事后尝试弗兰肯斯坦文本。可悲的是,生活并不总是那么简单。也许您有一个日志文件,您想要将连接事件与断开连接事件配对,或者有一个文件列出您想要将每个.c文件与其匹配.o文件配对,或者其他什么。无论出于何种原因,您最终都会遇到文本修改任务。

那么解决这个问题的最佳方法是什么?如何从多个单独的行收集字段,并将它们合并到同一行,但仍将记录分开? (即,不要把一切在一条巨大的线上,只有与相同的记录。)

我没有完成此类任务的通用方法。一些想法:

  • 您可以使用grep仅提取与正则表达式匹配的部分,从而提取您想要的字段。但是,我没有看到提取的方法全部有趣的字段而不运行grep多次,导致破坏字段之间的相对顺序。
  • sed使得仅提取匹配位变得更加困难,但确实允许将字段保持相同的相对顺序。现在你仍然需要以某种方式将字段收集到同一条线上。
  • awk有字段和记录的概念。我相信它可以进行正则表达式匹配。 (我真的不知道awk。)也许这就是解决这个问题的方法?虽然看起来相当复杂。
  • 你能用Python做点什么吗?这通常似乎已安装。我不知道它是否有任何正则表达式功能。

通常我最终会想出一些类似的东西作品,但它的可读性或可维护性从来都不是很高。我希望有一个更简单的方法。

答案1

两者awkpython适合这里。它们都支持关联数组(或字典),并且都内置正则表达式。

也许有一些通用的方法来处理它们,但我倾向于根据我可以使用的实际示例输入/输出进行优化。这是您的stat问题已解决awk

$ cat kv.awk 
/File:/ {
    f = $2
}

/Size:/ {
    info[f] = $2
}

/Access.*Uid/ {
    gsub(/\(|\/.*/, "", $2)
    info[f] = info[f] OFS $2
}

END {
    for (k in info) {
        print "VerifyFile(\"" k "\"", info[k] ");"
    }
}

文件名用作钥匙将与其相关的所有事物放在一起。最后,您可以按照您需要的任何格式显示结果。GNU awk也支持多维数组。

这是一个示例运行:

$ stat ip.txt test.txt > file.txt

$ awk -v OFS=', ' -f kv.awk file.txt
VerifyFile("'test.txt'", 254, 0664);
VerifyFile("'ip.txt'", 301, 0664);

为了学习awk,我建议https://www.gnu.org/software/gawk/manual/gawk.html其中涵盖GNU awk并提到了有关其他实现的细节。

相关内容