使用 Sed 添加文件文本

使用 Sed 添加文件文本
#!/bin/bash
search_string="\/sbin\/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT";
delimeters=$(cat /root/firewall/firewall.txt);
sed -i "s/$search_string/$delimeters$search_string/" /root/result.txt

我想将文件的内容添加到/root/firewall/firewall.txt保存/root/result.txt在变量中的行之前search_string

如果/root/firewall/firewall.txt包含上面一行,脚本就可以工作。但如果firewall.txt包含多行,脚本将中断为:

sed: -e expression #1, char 64: unterminated `s' command

我认为,新行字符导致了问题,但我无法正确反斜杠它。

search_string="\/sbin\/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT";
delimeters=$(cat /root/firewall/firewall.txt);
replaced= "$delimeters" | sed -r 's/\\n/\\\\n/g'
sed -i "s/$search_string/$replaced$search_string/" /root/result.txt 

我该如何解决这个问题?

答案1

(这与其他一些答案有些重叠。)

  1. 我有点困惑。你说,

    我想将文件的内容添加firewall.txtresult.txt文件 中一条线保存在search_string变量中。

    好的,首先,如果它们不是问题的重要组成部分,则完整路径名 ( /root/…) 会使您的问题变得混乱并且没有任何价值。只需使用简单的文件名。毕竟,我希望您在本地目录中调试它,不是以 root 身份运行。

    其次,什么有用的是给出一些示例数据。不是实际文件中实际拥有的 2000 行,而是少数行。 (再次:您正在测试文件上调试它,不是吗?)例如,假设result.txt包含

    One, two,
    /sbin/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT
    Buckle my shoe.
    

    firewall.txt包含

    Humpty Dumpty sat on a wall.
    

    你说,

    如果firewall.txt包含 [仅] 一行,则脚本

    #!/bin/bash
    search_string="\/sbin\/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT";
    delimeters=$(cat /root/firewall/firewall.txt);
    sed -i "s/$search_string/$delimeters$search_string/" /root/result.txt
    

    作品。

    (顺便说一句,您不需要在行尾使用分号,并且单词“delimiter”与中间的单词“limit”拼写在一起。)好吧,上面的结果是这样的:

    One, two,
    Humpty Dumpty sat on a wall./sbin/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT
    Buckle my shoe.
    

    真的是你想要的吗?因为当你说“将[文本]添加到[a]文件中时,大多数人并不是这么想的一条线”,特别是当您开始谈论要插入的文本超过一行时,特别是因为您说换行符应该仍然存在,就像在firewall.txt.我假设你真的想要

    One, two,
    Humpty Dumpty sat on a wall.
    /sbin/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT
    Buckle my shoe.
    

    如果您希望文件的最后一行firewall.txt与该/sbin/iptables行连接,请更准确地解释您想要的内容。

  2. 你说,

    search_string="\/sbin\/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT";
    delimeters=$(cat /root/firewall/firewall.txt);
    replaced= "$delimeters" | sed -r 's/\\n/\\\\n/g'
    sed -i "s/$search_string/$replaced$search_string/" /root/result.txt
    

    好吧,那是废话;第三行响应

    -bash: Humpty Dumpty sat on a wall.: command not found
    

    也许你的意思是

    替换=$(回声“$分隔符”| sed -r 's/\\n/\\\\n/g'

  3. 好吧,即使你说过

    replaced=$(echo "$delimeters" | sed -r 's/\\n/\\\\n/g')
    

    它不会有任何好处,因为sed通常一次运行一行,并且它不会将换行符视为行中的字符(即使它的输入来自具有多行值的 shell 变量;这与多行文件没有什么不同)。有什么作用(消除无用的使用cat) 是

    replaced=$(sed 's/$/\\/' firewall.txt)
    sed "s/$search_string/$replaced
    $search_string/" result.txt
    

    使用sed 's/$/\\/'在 中的每一行末尾添加反斜杠firewall.txt。您需要在之后键入换行符 ( Enter),$replaced 因为执行命令替换时最后一个换行符会被删除replaced=$(…)。而且,为了简单起见,如果您想保持命令/sbin/iptables不变,您可能需要考虑将最终命令更改为

    sed "s/$search_string/$replaced
    &/" result.txt
    

    在替换字符串中使用&表示“在此处插入搜索字符串(正则表达式)找到的文本”。

  4. 虽然该s命令允许您插入整行,但它并不是真正的目的。有ai、 和c命令用于从命令字符串插入一行或多行。但是,由于您的插入文本来自文件,因此查看r(read)命令。作为第一剪,

    sed "/$search_string/r firewall.txt" result.txt
    

    几乎会做你想做的事。几乎。不幸的是,它会firewall.txt在该行之后注入文件的内容/sbin/iptables。我能够找到一种解决方法(以获取firewall.txt行前的内容/sbin/iptables),但它非常复杂:

    sed -n -e "/$search_string/{s/^/+/; h; r firewall.txt
    n}" -e 'x; s/^+//p; s/.*//; x; p' result.txt
    

    显然sed不会识别;为结束文件名的分隔符,因此,当我们说 时r firewall.txt,我们必须键入Enter

    开始了:

    • -np:除非由或命令命令,否则不要写入输出r
    • /$search_string/{…}:对于每条匹配$search_string ( /sbin/iptables …) 的行,执行以下操作:
      • s/^/+/+在行首插入 a (创建+/sbin/iptables …)。这将该行标记为与搜索字符串匹配。 (我会回过头来讨论这一点。)
      • h:将模式空间(+/sbin/iptables …)复制到保留空间。
      • r firewall.txt:读取firewall.txt文件(并写入其内容)。
      • n:停止处理这一行并读取下一行。
    • 那么,对于每一个其他线(那些不是匹配$search_string),执行以下操作:
      • x:交换保持空间和模式空间的内容。即,我们刚刚读到的那一行(不是匹配$search_string)进入保存空间,然后我们将先前保存的行(可能是+/sbin/iptables …空白的)复制到模式空间中。
      • s/^+//p:如果该行是搜索字符串的已保存匹配项(即,它是已标记包含 ) 的行+/sbin/iptables …,剥离+并打印其余部分。否则,不打印任何内容。
      • s/.*//:擦掉线条(将所有内容替换为空)。我本来想做ddelete) 在这里,但这会终止当前行的处理。
      • x:再次交换保持空间和模式空间的内容。将空白行从模式空间移动到保留空间,并从中检索result.txt我们刚刚存放在那里的行。最后……
      • p:打印来自 的行result.txt

    简而言之,

    • 当我们找到匹配的行$search_string(即/sbin/iptables …)时,我们将其保存在保留空间中(不打印它),并读取(并打印)该firewall.txt文件。
    • 对于每个其他(即不匹配的)行,我们将保存的行(如果有)从保留空间中拉出并打印它,然后打印当前行。

    啊!如果发生在最后一行,则会失败/sbin/iptables …,因为它被保存在保留空间中,但没有后续的不匹配行来触发其提取。

    /sbin/iptables … 因此,让我们通过在末尾添加一条虚拟行,然后策略性地删除它来确保最后一行永远不会发生这种情况。

    echo >> result.txt
    sed -n -e "/$search_string/{s/^/+/; h; r firewall.txt
    n}" -e 'x; s/^+//p; $d; s/.*//; x; p' result.txt
    

    $d会导致最后一行被删除。 (我们可以使用$q并获得相同的效果。)

    如果有多iptables行,这确实有效。但是,是的,这正在变得有点混乱。我想 答案现在看起来并不那么糟糕。sed "s/$search_string/$replacednewline&/"

答案2

虽然这对于一些健美操来说可能是可能的sed,但我会使用另一种工具。以 Perl 为例:

search_string="\/sbin\/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT";
delimeters=$(cat /root/firewall/firewall.txt);
perl -i -pe "s/$search_string/$delimeters$search_string/" /root/result.txt

答案3

sed 有更多的命令s///——有一个r命令可以将另一个文件读入当前流。然而读取文件当前行,并且您想要放置它当前的线路,所以我们必须做一些调整。请注意,您不必转义搜索字符串中的斜杠,我们可以在 sed 中使用替代分隔符。

$ cat result.txt
foo
bar
/sbin/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT
baz

$ cat firewall.txt
firewall line 1
firewall line 2

$ search_string="/sbin/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT"

$ sed "\%${search_string}% {
                     # we have hit the search string:
    x                # move the current line to hold space
    r firewall.txt   # queue the file for reading
    N                # append the next line from results
                     # -> this triggers the actual file read and printing
    s/\n//           # the "x" command left behind a blank line so we remove it
    H                # append the current line to the hold space   
    g                # then bring the hold space to the pattern space
}" result.txt                
foo
bar
firewall line 1
firewall line 2
/sbin/iptables -A INPUT -p tcp --dport 12443 -j ACCEPT
baz

sed 命令可以缩减为

sed "\#${search_string}#{x;r firewall.txt
N;s/\n//;H;g}" result.txt 

GNU sed 似乎需要在文件名后添加换行符。

答案4

sed '/\-A INPUT -s 192.168.0.0\/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT/a CLIENTSCRIPT2="hello.sh"' file.txt

在这种情况下,我在 -A INPUT...etc 下的 file.txt 上添加了变量 CLIENTSCRIPT2="hello.sh"' 请注意语法中的反斜杠转义

相关内容