bash / sh 脚本替换文本文件中某些标签/字符串之间的文本

bash / sh 脚本替换文本文件中某些标签/字符串之间的文本

我正在创建将使用 /bin/sh 或 /bin/bash 脚本执行的可执行文件,
我有一个文件,其中包含这样的结构,配置文件中只有一个#start#end标记,我想替换之间的文本那些标签,

...

#start
FirewallRuleSet global {
    FirewallRule allow tcp to google.com
    FirewallRule allow tcp to facebook.com

#more rules
}
#end

FirewallRuleSet known-users {
    FirewallRule allow to 0.0.0.0/0
}

...

期望的输出将是,

...

#start
FirewallRuleSet global {
    FirewallRule allow tcp to google.com
    FirewallRule deny tcp to facebook.com
    FirewallRule deny tcp to twitter.com
    FirewallRule allow tcp to exaple.com



#more rules
}
#end

FirewallRuleSet known-users {
    FirewallRule allow to 0.0.0.0/0
}

...

如何用一些新文本替换#start之间的整个文本?#end我只想从此配置文件中添加或删除规则。

这是配置文件的一部分,我想修改该文本中允许的 url。

答案1

使用

sed '/#start/,/#end/替换命令'

例如,如果文件名为myconfig,并且您想在该部分中将“allow”替换为“deny”,您可以说

sed '/#start/,/#end/s/allow/deny/' myconfig

这将使文件保持不变,并在标准输出上显示修改后文件的外观。您可能应该首先这样做,以验证您的命令是否正确。如果您想实际更改文件,请添加-i选项:

sed -i '/#start/,/#end/s/allow/deny/' myconfig

如果您想更换所有的文本 (全部文本)在这两行之间,你可以做一些比卢卡斯的回答:

sed '/#start/,/#end/c\
新文本行 1\
新文本行 2\
        ︙\
新文本行n-1\
新文本行n(最后)' ←关闭报价;这里没有反斜杠

c是个Csed(and ed)中的hange 命令;它的意思是“替换整行”。你不能简单地让#start#end线保持不变。如果您想保留它们,则必须重新插入它们:

sed -i '/#start/,/#end/c\
#start\
FirewallRuleSet global {\
    FirewallRule allow tcp to google.com\
    FirewallRule deny tcp to facebook.com\
                      ︙                 \
\
#more rules\
}\
#end' myconfig

/#start/,/#end/指定范围 — 从包含 的第一行#start 到包含 的第一行#end。如果您需要查找包含这些字符串而不包含其他内容的行,请使用/^#start$/,/^#end$/.

答案2

基于G-Man的回答和评论:

sed -i '/#start/,/#end/ {
//!d
/#start/a\
some new text\
more lines\
end of new text (no backslash here!)
}' myconfig

解释:

  1. /#start/,/#end/ { .... }对文本“#start”和“#end”(含)之间的每一行执行大括号中的命令。比较一下G-Man的答案。
  2. a是附加命令。它仅在匹配“#start”的行上执行,以便添加新文本。它将追加行,直到不以反斜杠+换行符终止的行。

答案3

因为你有两种状态,即两条不同行的存在在这之间移动,这是 awk(或 perl 或 python)的工作。由于我当前的语言是 python,程序将如下所示:

import sys

rule_file = sys.argv[1]
new_rules = sys.argv[2]

mode = "save_rules"
for line in open(rule_file):
    line = line.strip()
    if line == "#start":
        print line
        print new_rules
        mode = "replace_rules"
    elif line == "#end":
        mode = "save_rules"
        print line
    elif mode == "save_rules":
        print line

现在将其保存到rule_replace.py并调用它

python rule_replace.py my_rule_file.txt 'FirewallRuleSet global {
FirewallRule allow tcp to google.com
FirewallRule deny tcp to facebook.com
FirewallRule deny tcp to twitter.com
FirewallRule allow tcp to exaple.com



#more rules
}' >new_rules.txt

当然,您不必将新规则放在命令行上,我假设您将它们放在 shell 变量中,然后调用将如下所示:

$ python rule_replace.py my_rule_file.txt $new_rules

请注意,此脚本虽然有效,但并不是生产中某些内容的解决方案。它不会捕获任何错误(例如,如果源文件不存在)。它假设不检查 #start 行后面总是跟着 #end 行,并且这些行与您所描述的完全相同。如果投入生产,它还可以使用一些日志记录。

答案4

根据 G-Man 的答案,以下是如何执行此操作并使用自定义分隔符和变量替换(由于复杂性,这并不是立即显而易见的)

给定 inputfile.txt:

Line1
Line2
<!-- /START_MARKER/ -->
Line4
Line5
Line6
<!-- /END_MARKER/ -->
Line8
Line9

使用这些命令/脚本:

customtext="Any kind of variable text here"

sed -i '\%<!-- /START_MARKER/ -->%,\%<!-- /END_MARKER/ -->%c\
foo '"$customtext"' abcxyz whatever' inputfile.txt

结果是:

Line1
Line2
Any kind of variable text here
Line8
Line9

这里发生了很多事情,解释如下:

'- 必须使用单引号来包裹所有内容

\%- 使用自定义分隔符时(例如,如果/您的标记必须使用不同的分隔符),则必须在此处对其进行转义

<!-- /START_MARKER/ -->- 文字开始标记,在本例中,它是用于演示目的的怪异示例。

%- sed 分隔符,未转义

,- 逗号 (?)

\%- 再次使用 sed 分隔符,这次必须转义

<!-- /END_MARKER/ -->- 文字结束标记

%c- sed 分隔符(未转义)和c

\- 反斜杠,表示应使用以下文本/行作为替换

foo- 一些替换文本

'"$customtext"'- 自定义变量文本,注意使用'然后"启用变量替换,然后"'返回自由文本模式

abcxyz whatever- 一些额外的替换文本

'- 结束单引号

inputfile.txt- 文件名

相关内容