shell 通过检查上一行和下一行替换一行内容

agxfikkp  于 12个月前  发布在  Shell
关注(0)|答案(4)|浏览(137)

我试图通过检查前一行和下一行来替换内容。如下所示。

<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>192.168.1.22</IpAdd>
<port>6789</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</User>

<IpAdd>any IP address</IpAdd>替换为<IpAdd>111.111.111.111</IpAdd>,下一行是<port>1123</port>,并且应该在<Info><pInfo>"eth0"</pInfo>部分下。
我尝试的是:

sed -E "/<Info>/{n;/<pInfo>/{n;s@<IpAdd>.*<([^\n]*\n[^\n]*<port>1123</port>)@<IpAdd>111.111.111.111<@}}" test.txt

要替换端口1123以上的行。如果我删除([^\n]*\n[^\n]*<port>1123</port>),那么它将替换Info下的两个ips。

<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>6789</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</User>

预期:

<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>192.168.1.22</IpAdd>
<port>6789</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</User>
b5buobof

b5buobof1#

使用任何awk:

$ cat tst.awk
/^<Info>/ {
    info = ""
    inInfo = 1
}
inInfo {
    info = (info ? info ORS : "") $0
    if ( /^<\/Info>/ ) {
        $0 = info
        if ( (/<pInfo>"eth0"<\/pInfo>/) && (/<port>1123<\/port>/) ) {
            sub(/<IpAdd>.*<\/IpAdd>/,"<IpAdd>111.111.111.111</IpAdd>")
        }
        inInfo = 0
    }
}
!inInfo
$ awk -f tst.awk file
<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>192.168.1.22</IpAdd>
<port>6789</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</User>

上面的代码假设Info块中的行的顺序实际上并不重要。

vybvopom

vybvopom2#

一个awk的想法:

eth='eth0'
port='1123'
ip='111.111.111.111'

awk -v eth="${eth}" -v port="${port}" -v newip="${ip}" '     # populate awk variables with OS variable varlues

function update_print() {
    if (block) {                                             # if block is not empty then ...

       # if block contains matches for our "eth" and "port" values then update the block with newip

       if (block ~ "<pInfo>\"" eth "\"</pInfo>"   &&   block ~ "<port>" port "</port>")
          sub(/<IpAdd>.*<\/IpAdd>/, "<IpAdd>" newip "</IpAdd>", block)

       print block                                           # print block
    }

}

$0 == "<Info>"  { inblock = 1 }                              # if start of a new block then set flag
$0 == "</Info>" { update_print()                             # if end of block then update and/or print block
                  block   = ""                               # reset our
                  inblock = 0                                # variables
                }
inblock         { block = block (block ? ORS : "") $0        # if flag is set then append current line to variable "block"
                  next                                       # skip to next input line
                }
1                                                            # if we get here it is because "inblock == 0"; "1" is always true so print current line
' test.xml

这产生:

<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>192.168.1.22</IpAdd>
<port>6789</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</User>
tquggr8v

tquggr8v3#

这可能对你有用(GNU sed):

sed -E '/<Info>/{:a;N;/<\/Info>$/!ba
        s/(<IpAdd>).*(<\/IpAdd>\n<port>1123<\/port>)/\1111.111.111.111\2/}' file

收集<Info></Info>之间的行,然后匹配集合中以<IpAddr>开始并以1123</port>结束的两行,并用新地址替换旧地址。

fkaflof6

fkaflof64#

TXR中的解决方案:
我扩大了数据,以测试更多的情况:这个模式在我的<Info>中出现了两次,我有一个<NotInfo>元素,其中出现了这个模式,但不能编辑:

$ cat xml
<NotInfo>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</NotInfo>
<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>192.168.1.22</IpAdd>
<port>6789</port>
</Info>
<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
<NotInfo>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</NotInfo>

它只在正确的地方编辑:

$ txr edit.txr xml
<NotInfo>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</NotInfo>
<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
</Info>
<Info>
<pInfo>"eth1"</pInfo>
<IpAdd>192.168.1.22</IpAdd>
<port>6789</port>
</Info>
<Info>
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
</Info>
<User>
<avl>"Name"</avl>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</User>
<NotInfo>
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
</NotInfo>

其中edit.txr

@(repeat)
@  (cases)
<Info>
@    (do (put-line "<Info>"))
@    (repeat)
@      (cases)
<pInfo>"eth0"</pInfo>
<IpAdd>192.168.1.2</IpAdd>
<port>1123</port>
@        (output)
<pInfo>"eth0"</pInfo>
<IpAdd>111.111.111.111</IpAdd>
<port>1123</port>
@        (end)
@      (or)
@        line
@        (do (put-line line))
@      (end)
@    (last)
</Info>
@    (end)
@  (or)
@    line
@    (do (put-line line))
@  (end)
@(end)

它的工作原理是,我们有一个外部@(repeat),它正在进行滑动模式扫描,寻找两种情况:一个以<Info>开始的块,或者一个匹配一行的后备情况,可以是任何东西。当回退情况发生时,输出该行。无论匹配的是什么,都会被消耗,因此扫描从下一行开始。
主要情况以<Info>的匹配开始。如果成功,则输出<Info>,并进行类似于外部扫描的嵌套扫描:查找要替换的三行模式,否则就只能使用并输出一行。内部@(repeat)在看到</Info>关闭标记时终止。

相关问题