【发布时间】:2022-12-18 08:06:17
【问题描述】:
我想使用 AWK 替换输入文件中的一组动态模式,如下所示:
one ^var^ two
^var^ three ^var^
four five six
seven eight ^var^
这是(相对)惯用的方法,逐行处理输入:
BEGIN {
FS = ""
vars["var"] = "yay!"
}
{
while (match($0, /\^[[:alnum:]_]+\^/)) {
var = substr($0, RSTART+1, RLENGTH-2)
$0 = substr($0, 1, RSTART-1) vars[var] substr($0, RSTART+RLENGTH)
}
print
}
在这种情况下,只有一个变量 ^var^ 被替换为 yay!,但可以有许多不同的变量并且映射是动态加载的。
它在我尝试过的所有 AWK 实现上都相当快,最慢的 (GoAWK) 大约需要 320 毫秒来处理一个 4.9MiB 的测试文件,示例输入连接 1000 次。
但是,我希望能够处理很长的行,或者在 ^ 定界符内有换行符,所以这里有一种方法可以一次读取整个输入,然后在遍历时打印其中的一部分:
BEGIN {
RS = "\x1"
FS = ""
vars["var"] = "yay!"
}
{
while (match($0, /\^[[:alnum:]_]+\^/)) {
printf "%s%s", substr($0, 1, RSTART-1), vars[substr($0, RSTART+1, RLENGTH-2)]
$0 = substr($0, RSTART+RLENGTH)
}
printf "%s", $0
}
但是,它明显更慢。只有 GoAWK 能够在大约 290 毫秒内处理同一个文件。 MAWK 需要 48 秒,NAWK - ~3 分钟,而 GAWK 非常慢 - 我没有等它完成,但已经超过 10 分钟了。
考虑到 $0 = substr($0, RSTART+RLENGTH) 行的效率低下,这可能是预料之中的。我想知道 GoAWK 是如何让它变得如此之快的——也许是一些写时复制的魔法?
无论如何,我想知道在 GAWK 和/或 MAWK 中是否有一种(理想情况下可移植的)方法可以快速完成此操作。我知道使用 Perl 单行代码或 Python 双行代码也可以实现同样的效果,但我需要在 AWK 中完成:)
【问题讨论】:
-
如果您可以提供更多样本输入和替换,可能会有所帮助,特别是解决您提到的变化;您是一次更换一个还是多次?如何将替换项加载到
awk...-v variable="value"或从文件中加载?如果从文件加载,您打算如何区分\n作为替换字符串的一部分与普通行定界符?您是否需要担心您的目标定界符 (^) 出现在正常数据中(在源数据或替换数据中)? -
如果目标定界符 (
^) 没有显示为实际数据的一部分,您可以考虑设置FS='^',然后遍历偶数字段 (for (i=2;i<=NF;i=i+2)) 以查找与替换列表的匹配项(例如,在提供的示例中$i=="var"和$i in vars)