我在 /etc/test/host.conf 中有一个这样的文件:
# this is a test
host = example.com
private = 192.168.1.1
# end of test
# this is a test
host = example.com
private = 192.168.1.2
# end of test
我想找到“private = 192.168.1.1”行并将其删除,同时我还想删除其前两行和后一行。
我之前搜索过,但是找不到适合我的 sed 命令。
答案1
要查找并删除“private = 192.168.1.1”行以及其前两行和后一行,可以使用以下命令:
# Set the file path
file_path="/etc/test/host.conf"
# Use the "grep" command to find the line containing "private = 192.168.1.1"
line_number=`grep -n "private = 192.168.1.1" $file_path`
# Extract the line number from the output of the "grep" command
line_number=${line_number%:*}
# Use the "sed" command to delete the line containing "private = 192.168.1.1" and the two lines before and one line after it
sed -i "$((line_number-2)),$((line_number+1))d" $file_path
第一条命令使用“grep”命令在指定路径的文件中查找包含“private = 192.168.1.1”的行号。第二条命令从“grep”命令的输出中提取行号。第三条命令使用“sed”命令删除包含“private = 192.168.1.1”的行以及其前两行和后一行。
答案2
解决方案sed
sed '
: start
/\n.*\n.*\n/ b ready
N
b start
: ready
s_.*\n.*\nprivate = 192\.168\.1\.1\n.*__
/./ P
D
' /etc/test/host.conf
解释
对于 中的多行操作sed
,“NPD”方案很有用。在您的例子中,我从: start
(start
任意标签)和b start
(“转到标签”)之间的循环开始。循环将行(N
)附加到模式空间,直到\n.*\n.*\n
匹配。b ready
只要模式空间中恰好有三个换行符(即四行;模式空间中的最后一行永远不会以换行符结尾),就会跳出循环。
: ready
当模式空间中恰好有四行时,代码就会到达。s
当且仅当第三行完全匹配时,Next 将用空值替换所有四行private = 192\.168\.1\.1
(点是特殊的,需要对其进行转义才能与点完全匹配)。
现在模式空间要么是空的,要么仍然包含四条线。
SoleP
打印模式空间直到第一个嵌入的换行符。如果没有嵌入的换行符,则P
工作方式与 类似p
,即打印整个模式空间和隐式换行符。现在:
- 如果我们的模式空间是空的(四行被删除了
s
),我们就不想要这个隐式的换行符; - 但是如果模式空间不为空,我们希望将四行窗口移动一行。
/./ P
是条件P
。/./
测试模式空间是否包含至少一个字符(它甚至可能是空行之间的换行符)。P
当且仅当测试成功时,才会执行该命令。如果测试失败,则此步骤中不会打印任何内容。
SoleD
删除模式空间直到第一个嵌入的换行符并重新启动循环(不读取新行输入)。但如果没有嵌入的换行符,则D
工作方式类似于d
,即删除整个模式空间并启动新循环(读取新行输入)。现在:
- 如果我们的模式空间是空的(四行被删除了
s
),那么D
将开始一个新的循环,一行新的输入将被放入模式空间; - 但如果模式空间不为空则将
D
删除P
刚刚打印的内容,模式空间将包含三行。
重新开始循环或开始新的循环意味着从头开始执行。无论模式空间中有一行还是三行,循环都会将行追加到模式空间,直到有四行,然后代码才会跳转到: ready
。这样四行窗口就会遍历整个文件。
笔记
模式空间的自动打印在这里完全按照您的预期工作。
P
负责在四行窗口前进时打印行;模式空间的自动打印负责在文件末尾打印剩余的行(如果有)。使用
-i
如果你愿意的话。代码可能像这样:sed -i.bak ' … ' /etc/test/host.conf
我建议你先进行测试
-i
。你写了:
我想找到该
private = 192.168.1.1
行并将其删除,同时还想删除其前两行和后一行。我的代码是“全有或全无”,它会以四行为一包来删除行。我的意思是,如果没有“后面的行”或者“前面的行”少于“两行”,那么甚至“该
private = 192.168.1.1
行”也不会被删除。代码不会在删除第一个匹配的行后停止,它能够删除多包行。
如果匹配的四行包重叠,则不会删除所有行。例如此代码片段:
foo bar # this is a test host = example.com private = 192.168.1.1 # overlapping host = example.edu private = 192.168.1.1 # end of test
将转换为:
foo bar host = example.edu private = 192.168.1.1 # end of test
因为在删除任何匹配的四行包之后,算法会读取至少四行新行,然后才能删除更多内容。
答案3
尝试使用 bash 脚本和 4 行循环缓冲区(数组):
#!/bin/bash
lines=()
while IFS=$'\n' read -r line ; do
# accumulate up to 4 lines in an array
lines+=("$line")
if [ ${#lines[@]} -eq 4 ] ; then
# if the pattern is detected in the 3rd line, drop the accumulated lines
if [ $(echo ${lines[2]} | grep "private = 192.168.1.1" | wc -l) -eq 1 ] ; then
lines=()
else
# otherwise output the 1st line and frop it from the array
echo "${lines[0]}"
lines=("${lines[@]:1}")
fi
fi
done
# at the end output the possible remaining lines
while [ ${#lines[@]} -gt 0 ] ; do
echo "${lines[0]}"
lines=("${lines[@]:1}")
done
但是,如果行包含双引号,则不起作用"
(编辑:实际上,看起来即使带引号也能起作用)