#!/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
(这与其他一些答案有些重叠。)
我有点困惑。你说,
我想将文件的内容添加
firewall.txt
到result.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
行连接,请更准确地解释您想要的内容。你说,
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')
?
好吧,即使你说过
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
在替换字符串中使用
&
表示“在此处插入搜索字符串(正则表达式)找到的文本”。虽然该
s
命令允许您插入整行,但它并不是真正的目的。有a
、i
、 和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。开始了:
-n
p
:除非由或命令命令,否则不要写入输出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/.*//
:擦掉线条(将所有内容替换为空)。我本来想做d
(delete) 在这里,但这会终止当前行的处理。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"' 请注意语法中的反斜杠转义