【发布时间】:2017-09-12 17:59:07
【问题描述】:
我想在文件中搜索模式“xxxx”并删除该模式之前的 5 行和该匹配之后的 6 行。我如何使用 Sed 做到这一点?
【问题讨论】:
我想在文件中搜索模式“xxxx”并删除该模式之前的 5 行和该匹配之后的 6 行。我如何使用 Sed 做到这一点?
【问题讨论】:
这可能对你有用(GNU sed):
sed ':a;N;s/\n/&/5;Ta;/xxxx/!{P;D};:b;N;s/\n/&/11;Tb;d' file
保持 5 行的滚动窗口,遇到指定字符串时再添加 6 行(总共 11 行)并删除。
注意这是一个barebones 解决方案,很可能需要根据您的特定需求进行定制。诸如:如果整个文件中有多个字符串怎么办?如果字符串在前五行之内,或者多个字符串在五行之内,等等等等。
【讨论】:
&/5 中的& 是什么意思,s/\n/&/11 或s/\n/&/5 中的每个小符号是什么意思?
s/\n/&/5 表示:如果出现换行符\n,则用其自身替换第五次出现(5 是命令末尾的标志)(&是命令左侧匹配内容的简写,即正则表达式)。
这是使用 awk 的一种方法。我假设您还想删除该行本身并且该文件足够小以适合内存:
awk '{a[NR]=$0}/xxxx/{f=NR}END{for(i=1;i<=NR;++i)if(i<f-5||i>f+6)print a[i]}' file
将每一行存储到数组a。当模式/xxxx/ 匹配时,保存行号。处理完整个文件后,循环遍历数组,只打印要保留的行。
或者,您可以先使用 grep 获取行号:
grep -n 'xxxx' file | awk -F: 'NR==FNR{f=$1}NR<f-5||NR>f+6' - file
在这两种情况下,删除的行将围绕模式匹配的最后一行。
第三种选择是使用 grep 获取行号,然后使用 sed 删除行:
line=$(grep -nm1 'xxxx' file | cut -d: -f1)
sed "$((line-5)),$((line+6))d" file
在这种情况下,我还添加了 -m 开关,以便 grep 在找到第一个匹配项后退出。
【讨论】:
如果你知道行号(不难获得),你可以使用类似的东西:
filename="test"
start=`expr $curr_line - 5`
end=`expr $curr_line + 6`
sed "${start},${end}d" $filename (optionally sed -i)
当然,您必须记住附加条件,例如 start 不应小于 1 并且 end 不应大于文件中的行数。
【讨论】:
另一个 - 可能更容易理解 - 解决方案是使用 grep 查找关键字和相应的行:
grep -n 'KEYWORD' <file>
然后使用sed 获取行号,如下所示:
grep -n 'KEYWORD' <file> | sed 's/:.*//'
现在您有了行号,只需像这样使用sed:
sed -i "$(LINE_START),$(LINE_END) d" <file>
删除之前和/或之后的行!仅使用-i,您将覆盖<file>(无备份)。
脚本示例可以是:
#!/bin/bash
KEYWORD=$1
LINES_BEFORE=$2
LINES_AFTER=$3
FILE=$4
LINE_NO=$(grep -n $KEYWORD $FILE | sed 's/:.*//' )
echo "Keyword found in line: $LINE_NO"
LINE_START=$(($LINE_NO-$LINES_BEFORE))
LINE_END=$(($LINE_NO+$LINES_AFTER))
echo "Deleting lines $LINE_START to $LINE_END!"
sed -i "$LINE_START,$LINE_END d" $FILE
请注意,这仅在关键字找到一次时才有效!根据您的需要调整脚本!
【讨论】: