关于shell:删除包含特定字符串的文本文件中的行

Delete lines in a text file that contain a specific string

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


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

1
sed '/pattern to match/d' ./infile

要直接修改文件:

1
sed -i '/pattern to match/d' ./infile

要直接修改文件(并创建备份):

1
sed -i.bak '/pattern to match/d' ./infile

对于Mac OS X和FreeBSD用户:

1
sed -i '' '/pattern/d' ./infile


sed外,还有许多其他方法可以删除具有特定字符串的行:

AWK

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

红宝石(1.9 +)

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

珀尔

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

Shell(bash 3.2及更高版本)

1
2
3
4
5
while read -r line
do
  [[ ! $line =~ pattern ]] && echo"$line"
done <file > o
mv o file

GNU-GRIP

1
grep -v"pattern" file > temp && mv temp file

当然,sed(打印反转比实际删除快):

1
sed -n '/pattern/!p' file


您可以使用sed替换文件中的行。但是,它似乎比将grep用于将反向文件转换为第二个文件,然后将第二个文件移到原始文件上慢得多。

例如

1
sed -i '/pattern/d' filename

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

不管怎样,第一个命令在我的机器上需要3倍的时间。


使用GNU sed可以很容易地做到这一点:

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


您可以考虑使用ex(这是一个标准的基于Unix命令的编辑器):

1
ex +g/match/d -cwq file

在哪里?

  • +执行给定的ex命令(man ex),与-c执行wq(写入和退出)
  • g/match/d—用给定的match删除行的ex命令,见:g的幂

上面的示例是一个符合POSIX的方法,用于根据本文的unix.se和ex的POSIX规范就地编辑文件。

sed的区别在于:

sed is a Stream EDitor, not a file editor.BashFAQ

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


我在Mac上苦苦挣扎。另外,我需要使用变量替换来完成它。

所以我用:

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

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

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

这里要注意的是在"/$pattern/d"中使用双引号。当我们使用单引号时,变量将不起作用。


要获得与grep类似的就地结果,可以执行以下操作:

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


我用一个包含大约345000行的文件做了一个小基准。在这种情况下,使用grep方法的速度大约是使用sed方法的15倍。

我尝试过在设置lc_all=c和不设置lc_all=c的情况下,似乎并没有显著改变计时。搜索字符串(cdga_00004.pdbqt.gz.tar)位于文件的中间。

以下是命令和时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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


您还可以使用:

1
 grep -v 'pattern' filename

这里,-v只打印您的模式以外的内容(这意味着反转匹配)。


塞德:

  • '/James\|John/d'
  • -n '/James\|John/!p'

AWK:

  • '!/James|John/'
  • /James|John/ {next;} {print}

GRP:

  • -v 'James\|John'

echo -e"/thing_to_delete
dd\033:x
" | vim file_to_edit.txt


1
2
perl -i    -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3

第一个命令编辑就地的文件(-i)。

第二个命令执行相同的操作,但通过在文件名中添加.bk来保留原始文件的副本或备份(可以将.bk更改为任何内容)。


1
2
cat filename | grep -v"pattern"> filename.1
mv filename.1 filename


为了防止有人想要对字符串进行精确匹配,您可以使用grep-w中的-w标志作为整体。例如,如果要删除编号为11的行,但保留编号为111的行:

1
2
3
4
5
6
7
8
9
10
11
-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标志一起使用。如果"黑名单"是一个文件,每行上有多个模式,您想从"文件"中删除:

1
grep -w -v -f blacklist file