我经常发现自己需要将文本从一种格式转换为另一种格式。一个典型的情况是我有一个包含一些信息的日志文件,我想找出该信息的子集,并以非常特定的方式格式化。
现在,如果每个“记录”都在同一行,则任务相对容易:
- 可以选择用于
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
两者awk
都python
适合这里。它们都支持关联数组(或字典),并且都内置正则表达式。
也许有一些通用的方法来处理它们,但我倾向于根据我可以使用的实际示例输入/输出进行优化。这是您的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
并提到了有关其他实现的细节。