如何从文本文件中删除所有包含特定字符串的行?

2314

我该如何使用sed删除一个文本文件中包含特定字符串的所有行?

21个回答

3501

要删除行并将输出打印到标准输出:

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

2
我怎样知道我使用的sed版本是GNU还是非GNU? - A Clockwork Orange
我不知道为什么,但是使用 least 命令结果得到了一个空文件 :( - marquies
1
使用GNU sed 4.2.2时,-i''根本不起作用。它会将要匹配的模式视为文件名。只需删除''即可。 - hookenz
1
在Ubuntu上,执行sed -i.bak "/str/d" ./infile命令会删除整个文件。 - chovy
6
请注意,如果您的模式将包含正斜杠,则可以使用替代模式定界符,但必须对第一个正斜杠进行转义,例如:sed -i.bak "\#$pattern_variable_containing_slashes#d" ./infile - Eric
显示剩余2条评论

722

除了sed之外,还有许多其他删除包含特定字符串的行的方法:

AWK

awk '!/pattern/' file > temp && mv temp file

Ruby (1.9+)

ruby -i.bak -ne 'print if not /test/' file

Perl

perl -ni.bak -e "print unless /pattern/" file

Shell(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

4
如何删除包含特定模式以及其上一行的行?我有一个文件,在不同的数据之间有成千上万条这样的行。 - oortcloud_domicile
2
在OS/X上,shell变量不能保留前导空格,但grep -v变量对我非常有效。 - Paul Beusterien
18
这个 sed 的例子有不同的行为,它只是查找匹配内容!应该改成类似这样的命令:sed -n -i '/pattern/!p' file。请注意,这个命令只会保留不匹配模式的行,并在原文件上进行修改。 - caesarsol
11
当每一行都匹配模式时,grep版本无法工作。更好的做法是:grep -v "pattern" file > temp; mv temp file 根据返回值情况,这个做法可能也适用于其他示例。 - Chris Maes
5
打印倒数比实际删除更快 - 在我的电脑上不是这样(2012年MacBook Air,OS X 10.13.2)。创建文件:seq -f %f 10000000 >foo.txt。sed d:time sed -i'' '/6543210/d' foo.txt,实际时间为0m9.294s。sed !p:time sed -i'' -n '/6543210/!p' foo.txt,实际时间为0m13.671s。(对于较小的文件,差异更大。) - jcsahnwaldt Reinstate Monica
显示剩余3条评论

331
你可以使用sed在文件中直接替换行。但是,它似乎比使用grep将反向内容写入第二个文件并将第二个文件移动到原始文件中要慢得多。
例如:
sed -i '/pattern/d' filename      

或者

grep -v "pattern" filename > filename2; mv filename2 filename

无论如何,在我的机器上第一个命令需要3倍的时间。


32
因为你尝试了性能比较,所以我会给你点赞! - anuragw
6
感谢提供覆盖当前文件的 grep 行选项,给你一个赞。 - Rhyuk
3
第二个“grep”解决方案对于大文件也更好。 - simoes
3
我很好奇如果使用 sed '/pattern/d' filename > filename2; mv filename2 filename 会有什么性能差异。 - Pete
14
使用Ubuntu的/usr/share/dict/words: grep和mv:0.010秒 | 原地sed:0.197秒 | sed和mv:0.031秒 - ReactiveRaven
显示剩余5条评论

109

使用GNU sed 的简易方法:

sed --in-place '/some string here/d' yourfile

83
这是一个对于那些在这个问答主题上迷惑并且对Shell脚本编写不熟悉的人的有用提示:虽然短选项在命令行上使用一次很好,但在脚本中应该优先选择长选项,因为它们更易读。 - Dennis
4
支持--in-place标志。我需要在受权限保护的文件上测试它。(必须进行一些用户清理。) - Bee Kay
10
请注意,长选项仅适用于GNU sed。Mac和BSD用户需要安装gsed才能以此方式执行。 - Matt
2
另一个提示:如果您的正则表达式似乎无法匹配,请尝试使用-r选项(或-E,具体取决于您的版本)。这将启用正则表达式元字符+?{...}(...)的使用。 - rjh
当您的磁盘没有更多空间且无法将文本复制到另一个文件时,这是正确的答案。这个命令执行了什么被询问的操作? - ferreirabraga
@ferreirabraga,这个选项指定文件将在原地编辑。GNU sed 通过创建临时文件并将输出发送到该文件而不是标准输出来实现此目的。 - CervEd

46

你可以考虑使用ex(一种基于标准Unix命令的编辑器):

ex +g/match/d -cwq file

其中:

  • + 执行给定的 Ex 命令(man ex),与执行 wq(写入并退出)的 -c 相同
  • g/match/d - 用于删除带有给定 match 的行的 Ex 命令,参见:Power of g

上面的示例是一种符合 POSIX 标准的方法,用于原地编辑文件,具体请参见 Unix.SE 上的帖子ex 的 POSIX 规范


sed 的区别在于:

sed 是一个流编辑器,而不是一个文件编辑器。BashFAQ

除非您喜欢不可移植的代码、I/O 开销和其他一些副作用。因此,一些参数(例如原地编辑/-i)是非标准的 FreeBSD 扩展,可能在其他操作系统上不可用。


5
太好了!当我输入 man ex 时,显示的是 vim 的说明文档,看来 exvim 的一部分。如果我理解正确的话,这意味着 match 的模式语法应该是 http://vimregex.com/,它与 POSIX 和 PCRE 风格有些相似但也有所不同? - Anentropic
1
“:g”是一个符合POSIX标准的命令,并且有一些轻微的差异,我猜测PCRE就是基于它开发的。 - kenorb
@kenorb,“I/O开销和其他一些不良副作用”能否详细说明一下?据我所知,ex使用临时文件,就像每个其他正常的工具一样,除了可能使用dd - CervEd

28

我在Mac上遇到了问题。而且,我需要使用变量替换来完成它。

所以我使用了:

sed -i '' "/$pattern/d" $file

其中$file是需要删除的文件,$pattern是要匹配删除的模式。

我从这个评论中选择了''

需要注意的是在"/$pattern/d"中使用双引号。如果使用单引号,则变量将不起作用。


4
Mac的sed命令在使用-i选项时需要加上一个参数。因此,如果你不想备份原始文件,仍需添加一个空字符串:-i '' - wisbucky
2
对于 shell 使用 sed -i "/$pattern/d" $file。谢谢您的回答。 - Ashwaq

23

您也可以使用这个:

 grep -v 'pattern' filename

这里的-v参数将只打印除了匹配模式之外的内容(也就是反向匹配)。


18

要使用grep获得类似于inplace的结果,您可以这样做:

echo "$(grep -v "pattern" filename)" >filename

4
这仅适用于 bash 或类似的 shell(不适用于 tcsh)。 - esmit

16

我用一份大约有345,000行的文件做了一个小型基准测试。在这种情况下,使用 grep 的方法似乎比使用 sed 方法快了约15倍。

我尝试过设置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

你使用的是哪个平台?你使用的sed/perl/grep版本是什么? - hagello
我使用的平台是Linux(Gentoo)。sed版本是GNU sed v 4.2.2,perl版本是perl 5(我无法确定测试时使用的修订版),grep(GNU)版本为3.0。 - Jadzia

15

删除所有匹配的文件中的行

grep -rl 'text_to_search' . | xargs sed -i '/text_to_search/d'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接