如何使用 sed 删除文本文件中包含特定字符串的所有行?
20 回答
要删除该行并将输出打印到标准输出:
sed '/pattern to match/d' ./infile
直接修改文件 - 不适用于 BSD sed:
sed -i '/pattern to match/d' ./infile
相同,但对于 BSD sed(Mac OS X 和 FreeBSD) - 不适用于 GNU sed:
sed -i '' '/pattern to match/d' ./infile
直接修改文件(并创建备份)——使用 BSD 和 GNU sed:
sed -i.bak '/pattern to match/d' ./infile
除了以下之外,还有许多其他方法可以删除具有特定字符串的行sed
:
AWK
awk '!/pattern/' file > temp && mv temp file
红宝石 (1.9+)
ruby -i.bak -ne 'print if not /test/' file
Perl
perl -ni.bak -e "print unless /pattern/" file
外壳(bash 3.2 及更高版本)
while read -r line
do
[[ ! $line =~ pattern ]] && echo "$line"
done <file > o
mv o file
GNU grep
grep -v "pattern" file > temp && mv temp file
当然sed
(打印倒数比实际删除更快):
sed -n '/pattern/!p' file
您可以使用 sed 替换文件中的行。但是,它似乎比使用 grep 反转到第二个文件然后将第二个文件移到原始文件上要慢得多。
例如
sed -i '/pattern/d' filename
或者
grep -v "pattern" filename > filename2; mv filename2 filename
无论如何,第一个命令在我的机器上花费了 3 倍的时间。
使用 GNU 的简单方法sed
:
sed --in-place '/some string here/d' yourfile
您可以考虑使用ex
(这是一个标准的基于命令的 Unix 编辑器):
ex +g/match/d -cwq file
在哪里:
+
执行给定的 Ex 命令 (man ex
),与-c
执行的相同wq
(写入和退出)g/match/d
- Ex 命令删除带有给定的行match
,请参阅:g 的幂
上面的示例是一种符合 POSIX 的方法,用于根据Unix.SE和POSIX 规范中的ex
这篇文章就地编辑文件。
区别sed
在于:
sed
是一个流媒体编辑器,而不是一个文件编辑器。Bash常见问题
除非您喜欢不可移植的代码、I/O 开销和其他一些不好的副作用。所以基本上一些参数(例如 in-place/ -i
)是非标准的 FreeBSD 扩展,可能在其他操作系统上不可用。
我在 Mac 上为此苦苦挣扎。另外,我需要使用变量替换来做到这一点。
所以我用:
sed -i '' "/$pattern/d" $file
where$file
是需要删除的文件,$pattern
是匹配删除的模式。
我''
从这个评论中挑选了。
这里要注意的是在"/$pattern/d"
. 当我们使用单引号时,变量将不起作用。
你也可以使用这个:
grep -v 'pattern' filename
这里-v
将只打印您的模式以外的内容(这意味着反转匹配)。
要获得类似的结果,grep
您可以这样做:
echo "$(grep -v "pattern" filename)" >filename
我用一个包含大约 345 000 行的文件做了一个小基准测试。在这种情况下,方法似乎比方法快grep
15 倍左右。sed
我已经尝试过设置和不设置 LC_ALL=C,它似乎并没有显着改变时间。搜索字符串 (CDGA_00004.pdbqt.gz.tar) 位于文件中间的某个位置。
以下是命令和时间:
time sed -i "/CDGA_00004.pdbqt.gz.tar/d" /tmp/input.txt
real 0m0.711s
user 0m0.179s
sys 0m0.530s
time perl -ni -e 'print unless /CDGA_00004.pdbqt.gz.tar/' /tmp/input.txt
real 0m0.105s
user 0m0.088s
sys 0m0.016s
time (grep -v CDGA_00004.pdbqt.gz.tar /tmp/input.txt > /tmp/input.tmp; mv /tmp/input.tmp /tmp/input.txt )
real 0m0.046s
user 0m0.014s
sys 0m0.019s
赛德:
AWK:
GREP:
perl -i -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3
第一个命令就地编辑文件 (-i)。
第二个命令做同样的事情,但通过将 .bk 添加到文件名来保留原始文件的副本或备份(.bk 可以更改为任何内容)。
您还可以删除文件中的一系列行。例如删除 SQL 文件中的存储过程。
sed '/CREATE PROCEDURE.*/,/END ;/d' sqllines.sql
这将删除 CREATE PROCEDURE 和 END ; 之间的所有行。
我已经用这个 sed 命令清理了许多 sql 文件。
从所有匹配的文件中删除行
grep -rl 'text_to_search' . | xargs sed -i '/text_to_search/d'
echo -e "/thing_to_delete\ndd\033:x\n" | vim file_to_edit.txt
以防万一有人想对字符串进行精确匹配,您可以使用-w
grep - w 中的标志作为整体。也就是说,例如,如果您想删除编号为 11 的行,但保留编号为 111 的行:
-bash-4.1$ head file
1
11
111
-bash-4.1$ grep -v "11" file
1
-bash-4.1$ grep -w -v "11" file
1
111
-f
如果您想一次排除几个确切的模式,它也可以与标志一起使用。如果“黑名单”是一个文件,每行都有多个模式,您想从“文件”中删除:
grep -w -v -f blacklist file
cat filename | grep -v "pattern" > filename.1
mv filename.1 filename
奇怪的是,接受的答案实际上并没有直接回答问题。该问题询问有关使用 sed 替换字符串的问题,但答案似乎预设了如何将任意字符串转换为正则表达式的知识。
许多编程语言库都具有执行这种转换的功能,例如
python: re.escape(STRING)
ruby: Regexp.escape(STRING)
java: Pattern.quote(STRING)
但是如何在命令行上做到这一点?
由于这是一个面向 sed 的问题,因此一种方法是使用 sed 本身:
sed 's/\([\[/({.*+^$?]\)/\\\1/g'
所以给定一个任意字符串 $STRING 我们可以写如下:
re=$(sed 's/\([\[({.*+^$?]\)/\\\1/g' <<< "$STRING")
sed "/$re/d" FILE
或作为单行:
sed "/$(sed 's/\([\[/({.*+^$?]\)/\\\1/g' <<< "$STRING")/d"
与本页其他地方所述的变化。
在控制台中显示处理后的文本
cat filename | sed '/text to remove/d'
将处理过的文本保存到文件中
cat filename | sed '/text to remove/d' > newfile
将处理过的文本信息附加到现有文件
cat filename | sed '/text to remove/d' >> newfile
处理已处理的文本,在这种情况下,删除更多已删除的行
cat filename | sed '/text to remove/d' | sed '/remove this too/d' | more
将| more
一次显示一页的文本。
您可以使用 good old以ed
与使用. 在这种情况下,最大的区别在于它通过标准输入获取命令,而不是像can 这样的命令行参数。在脚本中使用它时,通常的方法是使用管道将命令传递给它:ex
ed
ex
printf
printf "%s\n" "g/pattern/d" w | ed -s filename
或使用heredoc:
ed -s filename <<EOF
g/pattern/d
w
EOF