简体   繁体   English

使用 awk/sed 替换搜索字符串上方第 n 行的值

[英]Replace value in nth line ABOVE search string using awk/sed

I have a large firewall configuration file with sections like these distributed all over:我有一个大型防火墙配置文件,其中分布着这些部分:

edit 78231
  set srcintf "port1"
  set dstintf "any"
  set srcaddr "srcaddr" 
  set dstaddr "vip-dnat" 
  set service "service" 
  set schedule "always"
  set logtraffic all
  set logtraffic-start enable
  set status enable
  set action accept
 next

I want to replace value "port1" , which is 3 lines above search string "vip-dnat" .我想替换值"port1" ,它位于搜索字符串"vip-dnat"上方 3 行。

It seems the below solution is close but I don't seem to be able to invert the search to check above the matched string.下面的解决方案似乎很接近,但我似乎无法反转搜索以检查匹配的字符串上方。 Also it does not replace the value inside the file: Replace nth line below the searched pattern in a file它也不会替换文件内的值: Replace nth line below the searched pattern in a file

I'm able to extract the exact value using the following awk command but simply cannot figure out how to replace it within the file ( sub / gsub ?):我可以使用以下 awk 命令提取确切的值,但根本无法弄清楚如何在文件中替换它( sub / gsub ?):

awk -v N=3 -v pattern=".*vip-dnat.*" '{i=(1+(i%N));if (buffer[i]&& $0 ~ pattern) print buffer[i]; buffer[i]=$3;}' filename
"port1"

We could use tac + awk combination here.我们可以在这里使用tac + awk组合。 I have created a variable occur with value after how many lines(when "vip-dnat" is found) you need to perform substitution.我创建了一个变量,在您需要执行替换的行数之后(当找到"vip-dnat"时) occur值。

tac Input_file | 
awk -v occur="3" -v new_port="new_port_value" '
/\"vip-dnat\"/{
  found=1
  print
  next
}
found && ++count==occur{
  sub(/"port1"/,new_port)
  found=""
}
1' | 
tac

Explanation: Adding detailed explanation for above.说明:为上述添加详细说明。

tac Input_file |             ##Printing Input_file content in reverse order, sending output to awk command as an input.
awk -v occur="3" -v new_port="new_port_value" '  ##Starting awk program with 2 variables occur which has number of lines after we need to perform substitution and new_port what is new_port value we need to keep.
/\"vip-dnat\"/{              ##Checking if line has "vip-dnat" then do following.
  found=1                    ##Setting found to 1 here.
  print                      ##Printing current line here.
  next                       ##next will skip all statements from here.
}
found && ++count==occur{     ##Checking if found is SET and count value equals to occur.
  sub(/"port1"/,new_port)    ##Then substitute "port1" with new_port value here.
  found=""                   ##Nullify found here.
}
1' |                         ##Mentioning 1 will print current line and will send output to tac here.
tac                          ##Again using tac will print output in actual order.

Use a Perl one-liner.使用 Perl 单线。 In this example, it changes line number 3 above the matched string to set foo bar :在此示例中,它将匹配字符串上方的第 3 行更改为set foo bar

perl -0777 -pe 's{ (.*\n) (.*\n) ( (?:.*\n){2} .* vip-dnat ) }{${1}  set foo bar\n${3}}xms' in_file

Prints:印刷:

edit 78231
  set foo bar
  set dstintf "any"
  set srcaddr "srcaddr" 
  set dstaddr "vip-dnat" 
  set service "service" 
  set schedule "always"
  set logtraffic all
  set logtraffic-start enable
  set status enable
  set action accept
 next

When you are satisfied with the replacement written into STDOUT, change perl to perl -i.bak to replace the file in-place.当您对写入 STDOUT 的替换感到满意时,将perl更改为perl -i.bak以就地替换文件。

The Perl one-liner uses these command line flags: Perl 单行代码使用这些命令行标志:
-e : Tells Perl to look for code in-line, instead of in a file. -e :告诉 Perl 查找内联代码,而不是在文件中。
-p : Loop over the input one line at a time, assigning it to $_ by default. -p :一次循环输入一行,默认将其分配给$_ Add print $_ after each loop iteration.在每次循环迭代后添加print $_
-i.bak : Edit input files in-place (overwrite the input file). -i.bak :就地编辑输入文件(覆盖输入文件)。 Before overwriting, save a backup copy of the original file by appending to its name the extension .bak .在覆盖之前,通过在其名称后附加扩展名.bak来保存原始文件的备份副本。 -0777 : Slurp files whole. -0777 : 整个 Slurp 文件。

(.*\n) : Any character, repeated 0 or more times, ending with a newline. (.*\n) :任何字符,重复 0 次或多次,以换行符结尾。 Parenthesis serve to capture the matched part into "match variables", numbered $1 , $2 , etc, from left to right according to the position of the opening parenthesis.括号用于将匹配的部分捕获为“匹配变量”,根据左括号的 position 从左到右编号为$1$2等。
( (?:.*\n){2}.* vip-dnat ) : 2 lines followed by the line with the desired string vip-dnat . ( (?:.*\n){2}.* vip-dnat ) : 2 行,后跟所需字符串vip-dnat的行。 (?: ... ) represents non-capturing parentheses. (?: ... )表示非捕获括号。

SEE ALSO:也可以看看:
perldoc perlrun : how to execute the Perl interpreter: command line switchesperldoc perlrun :如何执行 Perl 解释器:命令行开关
perldoc perlre : Perl regular expressions (regexes) perldoc perlre : Perl 正则表达式(正则表达式)
perldoc perlre : Perl regular expressions (regexes): Quantifiers; perldoc perlre : Perl 正则表达式(正则表达式):量词; Character Classes and other Special Escapes;字符类和其他特殊转义; Assertions;断言; Capture groups捕获组
perldoc perlrequick : Perl regular expressions quick start perldoc perlrequick : Perl 正则表达式快速入门

The regex uses these modifiers:正则表达式使用这些修饰符:
/x : Ignore whitespace and comments, for readability. /x :为了可读性,忽略空格和注释。
/m : Allow multiline matches. /m :允许多行匹配。
/s : Allow . /s :允许. to match a newline.匹配换行符。

Whenever you have tag-value pairs in your data it's best to first create an array of that mapping ( tag2val[] below) and then you can test and/or change and/or print the values in whatever order you like just be using their names:每当您的数据中有标签值对时,最好先创建一个该映射的数组(下面的tag2val[] ),然后您可以按照您喜欢的任何顺序测试和/或更改和/或打印值,只是使用它们名称:

$ cat tst.awk
$1 == "edit" { editId=$2; next }
editId != "" {
    if ($1 == "next") {

        # Here is where you test and/or set the values of whatever tags
        # you like by referencing their names.
        if ( tag2val[ifTag] == ifVal ) {
            tag2val[thenTag] = thenVal
        }

        print "edit", editId
        for (tagNr=1; tagNr<=numTags; tagNr++) {
            tag = tags[tagNr]
            val = tag2val[tag]
            print "  set", tag, val
        }
        print $1
        editId = ""
        numTags = 0
        delete tag2val
    }
    else {
        tag = $2
        sub(/^[[:space:]]*([^[:space:]]+[[:space:]]+){2}/,"")
        sub(/[[:space:]]+$/,"")
        val = $0
        if ( !(tag in tag2val) ) {
            tags[++numTags] = tag
        }
        tag2val[tag] = val
    }
}

$ awk -v ifTag='dstaddr' -v ifVal='"vip-dnat"' -v thenTag='srcintf' -v thenVal='"foobar"' -f tst.awk file
edit 78231
  set srcintf "foobar"
  set dstintf "any"
  set srcaddr "srcaddr"
  set dstaddr "vip-dnat"
  set service "service"
  set schedule "always"
  set logtraffic all
  set logtraffic-start enable
  set status enable
  set action accept
next

Note that the above approach:注意上面的方法:

  1. Will work even if/when either of the values you want to find appear in other contexts (eg associated with other tags or as substrings of other values), and doesn't rely on how many lines are between the lines you're interested in or what order they appear in in the record.即使/当您要查找的任何一个值出现在其他上下文中(例如与其他标签相关联或作为其他值的子字符串)时,也可以工作,并且不依赖于您感兴趣的行之间有多少行或者它们在记录中出现的顺序。
  2. Will let you change that value of any tag based on the value of any other tag and is easily extended to do compound comparisons, assignments, etc. and/or print the values in a different order or print a subset of them or add new tags+values.将允许您根据任何其他标签的值更改任何标签的值,并且可以轻松扩展以进行复合比较、分配等和/或以不同的顺序打印值或打印它们的子集或添加新标签+价值观。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM