使用 sed 在文件中查找并替换模式匹配后的第一次出现的文本

使用 sed 在文件中查找并替换模式匹配后的第一次出现的文本

假设我有以下输入文件:

Server 'Test AB'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test CD'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test EF'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test GH'
    option type 'ss'
    option port '1234'
    option timeout '60'

现在我想使用 sed 将“选项端口‘1234’”与“选项端口‘9876’”交换,但我只想对服务器“Test EF”执行此操作。所有其他端口应保持不变。

输出应如下所示:

Server 'Test AB'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test CD'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test EF'
    option type 'ss'
    option port '9876'
    option timeout '60'

Server 'Test GH'
    option type 'ss'
    option port '1234'
    option timeout '60'

我已经尝试了很多 sed 命令,我发现我可以使用

sed '11,15s/port '1234'/port '9876'/g' file

但这对我没什么帮助,因为服务器“Test EF”不会总是在同一行。我需要限制区域而不是 11,15,例如“'Test EF',选项超时”,但我不知道如何实现它。

答案1

与什么相反上一个答案说,这正合sed你的心意。而且,与另一条建议(现已删除)相反,这很容易在 单身的 sed过程。

sed "/Server 'Test EF'/,/Server/ s/option port '1234'/option port '9876'/" file

会做你想做的事。

有人说sed代码通常很神秘,除非你是 sed专家。虽然代码可能很难写,但我相信它相当容易阅读和理解:

  • 从包含以下内容的行开始Server 'Test EF'
  • 并继续下一行Server,其中包含
  • 搜索每一行option port '1234'
  • 如果找到的话,请替换option port '9876'

你其实已经很接近了。引用是你的尝试的一个大问题:你不能使用单引号字符 (')在字符串中也是分隔用单引号引起来(即以单引号开头和结尾)。处理此问题的一种方法是将字符串括在双引号字符中("); 比如

  … "…                         … s/option port '1234'/option port '9876'/" …
    ↑                                                                    ↑

如果字符串不包含任何"字符 — 或$\`。如果您使用 bash(如您用标签所示),则可以使用$'……\'……';例如,

echo $'the cat\'s pajamas'
the cat's pajamas

所以你可以

  … $'…                     … s/option port \'1234\'/option port \'9876\'/' …
                                            ⇑⇑    ⇑⇑            ⇑⇑    ⇑⇑

以下是在 awk 中执行此操作的一种不太隐秘的方法:

awk $'
    /Server/             { matched=0 }
    /Server \'Test EF\'/ { matched=1 }
    matched              { gsub("option port \'1234\'", "option port \'9876\'") }
                         { print }
  '
  • matched定义一个名为that is 1 (only) 当我们处于一个节中时才使用的变量Server 'Test EF'
  • 将其设置为1当我们匹配时Server 'Test EF'...
  • 0…当我们看到任何其他包含的行时,将其设置为Server
  • matched非零时(即当我们在一个Server 'Test EF'节中时),在每一行中搜索option port '1234' 并替换option port '9876'

答案2

这是正确的awk's盟友:

awk '$1 ~ q"Test EF"q { $3 = "    option port "q"9876"q } 1' \
  RS= FS='\n' q="'" OFS='\n' ORS='\n\n'

通过设置RS为空字符串,awk 一次读取整个“记录”。通过设置FS为换行符,每一行都是一个字段。

输出:

Server 'Test AB'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test CD'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test EF'
    option type 'ss'
    option port '9876'
    option timeout '60'

Server 'Test GH'
    option type 'ss'
    option port '1234'
    option timeout '60'

答案3

awk 'NF==/Test EF/ && ++_||!_||(NF = NF)^--_' FS=1234 OFS=9876

Server 'Test AB'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test CD'
    option type 'ss'
    option port '1234'
    option timeout '60'

Server 'Test EF'
    option type 'ss'
    option port '9876'
    option timeout '60'

Server 'Test GH'
    option type 'ss'
    option port '1234'
    option timeout '60'

相关内容