awk -- 在同一行中的直接模式之后替换一次!

awk -- 在同一行中的直接模式之后替换一次!

我们有一个 1.5MB 的文件。
某一行大约有 160000 个字符。
在该行中,我们只需在 PATTERN 之后立即将“false”更改为“true”一次。

awk 之后仅更改第一次出现的情况。
但我们只需要在“PATTERN”一次之后进行更改。

awk '/PATTERN.*false/ {sub("false", "true")} {print}' file

我们的文件包含:

...
colorA is false colorB is false PATTERN is false colorC is false colorD is false
...

文件需要有:

...
colorA is false colorB is false PATTERN is true colorC is false colorD is false
...

我们如何安排 awk 命令来满足我们的需要?

提前致谢!

答案1

虽然理论上可以编写一个匹配不匹配“false”的最短字符串的正则表达式,但这很困难。其他各种语言都有选择最短的语法,但 awk 不是其中之一。

因此,假设您不想编写硬表达式,可以使用 3 种方法。

  1. 您可以将输入拆分为单词“false”,循环遍历字段,如果当前字段与模式匹配,则将其打印出来,后跟“true”,否则打印出来,后跟“false”。
  2. 您可以使用一个技巧,将所有出现的“false”转换为单个未使用的字符,例如 control-a,然后正则表达式很容易编写,如您可以说 [^\001]*\001 来匹配以结尾的最短模式控制-a。
  3. 您只需循环遍历字段,如果当前字段是您的模式,则设置一个标志,如果它是“假”并且设置了标志,则将其更改为“真”并重置标志。

对于方法 3。

#!/usr/bin/awk

/PATTERN.*false/ {
        for(i=1;i<=NF;i++) {
                if ($i ~ /PATTERN/) flag=1;
                if ($i == "false" && flag==1) {
                        $i="true"
                        flag=0
                }
        }
}
{print}

这将折叠输入中的空白。

答案2

PATTERN该解决方案实现了在( )处分割线split、替换false第二部分 ( sub) 中的第一个部分并组合各部分(for循环 和printf)的方法。该next命令跳过对此输入行的进一步处理。其他行打印不变。 (1是具有默认操作的始终为真条件。)

awk '/PATTERN.*false/ {
    n=split($0,parts,"PATTERN"); 
    sub("false", "true", parts[2]); 
    for(i=1;i<n;i++) {
        printf("%s%s", parts[i], "PATTERN");
    }
    printf("%s\n", parts[n]);
    next }
1'

从问题中不清楚对应的值是否PATTERN总是false,因此它可能会替换错误false

输入样本

colorA is false colorB is false PATTERN is false colorC is false colorD is false
colorA is false colorB is false PATTERN is true colorC is false colorD is false

结果是这个输出

colorA is false colorB is false PATTERN is true colorC is false colorD is false
colorA is false colorB is false PATTERN is true colorC is true colorD is false

编辑根据鲁迪克的注释:如果 PATTERN 之后要修改的值是“true”或“false”,那么可以通过将指令替换为来避免这种可能的sub("false", "true", parts[2]);问题sub("false|true", "true", parts[2]);

awk '/PATTERN.*false/ {
    n=split($0,parts,"PATTERN"); 
    sub("false|true", "true", parts[2]); 
    for(i=1;i<n;i++) {
        printf("%s%s", parts[i], "PATTERN");
    }
    printf("%s\n", parts[n]);
    next }
1'

使用相同的样本输入,结果是

colorA is false colorB is false PATTERN is true colorC is false colorD is false
colorA is false colorB is false PATTERN is true colorC is false colorD is false

答案3

使用 GNU awk 将第三个参数传递给 match() 和 gensub():

$ awk 'match($0,/(.*PATTERN)(.*)/,a){$0=a[1] gensub(/false/,"true",1,a[2])} 1' file
...
colorA is false colorB is false PATTERN is true colorC is false colorD is false
...

以及任何 awk:

$ awk 'match($0,/.*PATTERN/){tail=substr($0,RSTART+RLENGTH); sub(/false/,"true",tail); $0=substr($0,1,RSTART+RLENGTH-1) tail } 1' file
...
colorA is false colorB is false PATTERN is true colorC is false colorD is false
...

相关内容