如何在匹配 grep 模式后编辑整个文件?

如何在匹配 grep 模式后编辑整个文件?

为了简化,我想在匹配模式后编辑整个文件,文件示例:

$ cat file
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=x.x.x.x
blahblah
mask=255.0.0.0
host=d

假设我想编辑主机 c 的 IP,但请注意,这可能是可变的,所以我不知道这个值。如果我grep host c使用 来-B2打印行,我无法在原始文件中编辑它!另一点,该行可能不具有相同的结构,这就是主机 d 的情况,在ipmaskinfo 之间有一些文本,所以我不能假设 IP 模式总是在我的搜索模式之前 2 行。

继续,我无法直接 grep IP,因为我不知道它,而是需要搜索主机,并编辑此匹配之前的行以更改 IP 的值。我怎样才能做到这一点?

答案1

如果您向后浏览该文件,这会变得容易得多。幸运的是,您可以轻松地做到这一点tac(这恰好是cat向后的)。然后我们可以使用一个相对简单的awk脚本来查找您的host,并仅更改其ip

$ tac input | awk -v OFS="=" -v myip="changed_address" -v myhost="d" -F"=" '$1 == "host" { if( $2 == myhost ) { sw = "on" } else { sw="off" } } sw == "on" && $1 == "ip" { $2=myip } { print $0 }' | tac
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=changed_address
blahblah
mask=255.0.0.0
host=d

我将详细解释其awk工作原理:

首先,我们声明几个变量: 各一个变量host, 和 的新值ip

-v myip="changed_address" -v myhost="d"

此外,我们声明输入和输出的字段分隔符:

-v OFS="=" -F"="

现在,实际的awk脚本本身:

$1 == "host" {             // If we see the "host" line..
  if( $2 == myhost ) {     // And it matches the one we're looking for..
    sw = "on"              // Set a flag to swap the next IP
  } else { 
    sw="off"               // Otherwise, unset the flag
  }
} 

sw == "on" && $1 == "ip" { // If the flag is set and this is an IP line.. 
    $2=myip                // Swap in the new IP 
}

{
   print $0                // Finally, print out the processed line
}

全部完成后,我们只需tac再次使用即可重新反转它,使其再次前进。

答案2

这会将与主机关联的 IP 更改c1.2.3.4

$ sed 's/^ip/\nip/' file | perl -00pe 'if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/} s/\n\n/\n/' 
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=1.2.3.4
mask=255.0.0.0
host=c
ip=x.x.x.x
blahblah
mask=255.0.0.0
host=d

解释:

  • sed 's/^ip/\nip/' file\n:向以 开头的每一行添加一个额外的换行符 ( ) ip。我认为这可能不适用于 的所有实现sed,因此如果您的不支持此功能,请将sed命令替换为perl -pe 's/^ip/\nip/'.我们需要这个才能使用 Perl 的“段落模式”(如下所示)。

  • perl -00pe-00使 perl 在“段落模式”下运行,其中“行”由两个连续的换行符定义。这使我们能够将每个主机的块视为一条“线”。意思-pe是“在应用给定的脚本后打印每一行-e”。

  • if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/}:如果此“行”(部分)匹配一个换行符,后跟该字符串host=c,然后是另一个换行符,则将其后面的ip=1 个或多个非空白字符 ( ) 替换为。\S+ip=1.2.3.4

  • s/\n\n/\n/将每对换行符替换为单个换行符以恢复原始文件的格式。

如果您希望更改文件,则可以使用:

tmp=$(mktemp); sed 's/^ip/\nip/' file > $tmp; 
perl -00pe 'if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/} s/\n\n/\n/' $tmp > file

答案3

另一种选择是将一些命令发送到ed

h=c
ed -s file <<< $'/^host='"${h}"$'$\n?^ip=\nc\nip=new.ip.here\n.\nw\nq' > /dev/null

基本思想是在以换行符分隔的命令列表(在两个 ANSI C 引号字符串中)中编辑文件和管道,$' ... '搜索给定的主机(在变量中$h),然后向后搜索以 开头的行ip=,然后更改它行成为新内容,然后保存并退出ed。由换行符 ( ) 分隔\n,命令为:

  1. /^host=... $h $-- 开始搜索 ( )行 ( ) 开头的/字符串,然后搜索 的内容,最后搜索 ( ) 行结束。如果您的主机不完全匹配,请放宽行尾要求。host=^$h$

  2. ?^ip=-- 现在我们位于匹配行上,向后搜索ip=行开头的文本。

  3. c-- 改变这一行

  4. 插入文本ip=new.ip.here

  5. .-- 结束插入文本

  6. w-- 将文件写入磁盘

  7. q——退出编辑

调用 ed with-s会静音打开和保存文件时发出的字节计数。当找到我们正在搜索的和行时,重定向整个命令以/dev/null静音默认输出。edhost=ip=

答案4

$ awk -v host=c -v newip=zzz.zzz.zzz.zzz '$0 ~ "^host=" host "$" { print; getline; $0 = sprintf("ip=%s\s", newip) }; 1' file
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=zzz.zzz.zzz.zzzs
blahblah
mask=255.0.0.0
host=d

这假设您想要更改某个指定主机的 IP 地址,并且 IP 地址行始终出现在host=该主机的行之后。

该程序通过设置两个变量和awk来在命令行上获取主机名和新的 IP 地址。然后,代码找到与给定主机名对应的行,读取并丢弃下一行(该行),并使用新的 IP 地址创建一个新行。数据(修改或未修改)由程序中的尾部输出。awkhostnewiphost=ip=ip=1

相关内容