为了简化,我想在匹配模式后编辑整个文件,文件示例:
$ 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 的情况,在ip
和mask
info 之间有一些文本,所以我不能假设 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 更改c
为1.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
,命令为:
/^host=
...$h
$
-- 开始搜索 ( )行 ( ) 开头的/
字符串,然后搜索 的内容,最后搜索 ( ) 行结束。如果您的主机不完全匹配,请放宽行尾要求。host=
^
$h
$
?^ip=
-- 现在我们位于匹配行上,向后搜索ip=
行开头的文本。c
-- 改变这一行插入文本
ip=new.ip.here
.
-- 结束插入文本w
-- 将文件写入磁盘q
——退出编辑
调用 ed with-s
会静音打开和保存文件时发出的字节计数。当找到我们正在搜索的和行时,重定向整个命令以/dev/null
静音默认输出。ed
host=
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 地址创建一个新行。数据(修改或未修改)由程序中的尾部输出。awk
host
newip
host=
ip=
ip=
1