INI 文件是 Firefox 的配置文件.ini。我想打印下面的所有内容,包括部分标题,其中是非负整数,每个部分之间用空行分隔。还有一个可选键,它与配置文件部分下的键不同,如果存在,我也想打印它。基本上,shell 脚本只是和之前(打印、以及可选的键和值),但如果添加或删除键,这将失败,并且grep 选项不可移植。[Profile
n
]
n
Default
[Install*]
Default
grep -E 'Default=[^1]' profiles.ini
grep -A4 '^\[Profile' profiles.ini
Name
IsRelative
Path
Default
-A
下面是我的一个 hack 解决方案,它既不是惯用的 AWK,也不是健壮的 AWK:
/^[[]Profile[0123456789]{1,}[]]$/ {
print
while ((getline) > 0) {
if ($0 ~ /^$/) { # Should really break on new sections.
print ""
break
} else {
print
}
}
}
/^Default=/ {
print # Default profile path given in the Install* section.
}
示例输入:
[Profile2]
Name=default-test
IsRelative=0
Path=/home/user/ffprofiles/f9bwn86n.default-test
[Profile1]
Name=default
IsRelative=1
Path=x64qf7nv.default
Default=1
[Profile0]
Name=default-release
IsRelative=1
Path=9hv1fbkk.default-release-3426201712696
[General]
StartWithLastProfile=1
Version=2
[Install22379532B4E49482]
Default=9hv1fbkk.default-release-3426201712696
Locked=1
示例输出:
[Profile2]
Name=default-test
IsRelative=0
Path=/home/user/ffprofiles/f9bwn86n.default-test
[Profile1]
Name=default
IsRelative=1
Path=x64qf7nv.default
Default=1
[Profile0]
Name=default-release
IsRelative=1
Path=9hv1fbkk.default-release-3426201712696
Default=9hv1fbkk.default-release-3426201712696
我怎样才能更简洁、更正确地做到这一点?解决方案实际上不必使用 AWK,但我认为 awk 比 sed 或任何其他 Unix 实用程序更适合这种情况。但是,解决方案必须是可移植的并且符合 POSIX 标准。提前谢谢您。
awk 能够在不需要 getline() 的情况下保持行之间的状态。如果您需要基于 awk 的解决方案,最简单的方法是:
作为单个 awk 脚本,这可能会起作用:
总是保存标题并进行字符串比较可能会使其更清晰,但也可能产生相反的结果。(我根本没有测试过这个版本。)
使用任何 awk:
您所描述的是一种具有上下文的结构化文本文件格式。
awk
可能(将)能够提取特定的部分,但它需要做出很多假设,这些假设不是基于文件格式的实际工作方式,而是基于您想到的具体示例的样子(从“简单”开始解决诸如“大写是否重要”之类的问题,到更有趣的事情,例如“我如何处理两个具有相同名称和重叠键的部分?”)。因此,不要使用
awk
或sed
任何其他主要与上下文无关的解析器方法来解析 ini 文件之类的文件。请使用了解格式的解析器。顺便说一下,这里的格式是TOML 。
您使用posix,这表示您可以使用 C99 编译器(这是与 一样 POSIX 的工具
awk
!)。因此,不要使用awk
基于 的 TOML 解析器,而要使用成熟且运行良好的 TOML 库。toml-c
是一个您可以直接将其作为头文件放在 .c 文件旁边的库。 该examples/
目录有两个示例,您可以直接将其调整到您的用例;只需将其替换toml_parse(char*,…)
为toml_parse_file(FILE*,…)
,然后打开您传递的文件argv[1]
;很简单。不会因为您认为 POSIX 实用程序是实现可移植性的途径而提供完整的 C99 解决方案 —— 坦率地说,遗憾的是事实并非如此;同一 POSIX 实用程序的不同实现在平台之间的不兼容性通常比 Python 解释器更严重,并且您预装的每个平台
awk
很可能也有一个python
。说到 Python,它附带一个 toml 解析器,下面是您的 9 行脚本,包括使用帮助,用于打印给定部分的所有键/值。由于当您给它一个输入中不存在的部分名称时,它会出错并返回非零值,因此可以使用一个简单的 shell 循环来处理
Profile0
,Profile1
... 直到ProfileN+1
不再存在。更优雅的做法是实际在 Python 本身中执行此操作,但这留给读者作为示例,因为那将非常具体,而此工具更普遍有用:只要您愿意一次提取一个密钥块,就可以使用单个 awk 命令。每个块的结尾很方便地是另一个 [key],或文件结尾。
下面是提取 [Profile0] 的命令:
或者提取以 [General] 开头的块:
在这些示例中,我使用了[Gen和[Profile0。请用您想要的 [key] 中足够的字符替换它们,以唯一地标识该块。
文件 ini.txt 是您的输入,即:
使用
awk
:以下两个命令可移植到任何能够运行Andrey Kislyuk 的无处不在的JSON 解析器的
tomlq
包装器jq
的 Unix 系统,我相信这比可以运行 Firefox 的系统更广泛。我唯一的假设是,a 之后的任何字符串
=
已经是充分编码的 TOML 字符串(不带双引号)。首先,可以通过引用所有值将输入转换为标准 TOML 格式:
然后我们可以提取以下
Profile
部分:鉴于问题中的例子,这应该给我们
如果您不喜欢等号周围的空格或添加的引号,请通过将输出传递过去来删除它们
sed 's/ = "\(.*\)"/=\1/'
。然后,我们可以单独获取该
Install
部分的Default
值(如果存在):...或者,如果你不介意为不存在的数据获取一个空行,那么
鉴于您的示例输入,这应该为您提供简单的值
(我们不能将这两个命令结合起来,因为 TOML 不能在顶层表示非对象。)
使用这种方法,您可以轻松提取具有与该部分的
Path
值相对应的值的部分(在示例中):Install
Default
Profile0