ChatGPT解决这个技术问题 Extra ChatGPT

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

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


F
Felix Rabe

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

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

谢谢,但它似乎并没有从文件中删除它,而只是打印出没有该字符串的文本文件内容。
@A Clockwork:是的,您需要将输出重定向到具有类似 sed '/pattern to match/d' ./infile > ./newfile 的新文件,或者如果您想进行就地编辑,则可以将 -i 标志添加到 sed 中,如 sed -i '/pattern to match/d' ./infile .请注意,-i 标志需要 GNU sed 并且不可移植
对于一些味道的 sed; sed 的“-i”标志需要提供扩展。 (例如sed -i.backup '/pattern to match/d' ./infile)这让我了解了就地编辑。
@SiegeX 更好的是,不要将 sed 之类的命令应用于任何不受版本控制的文件。
Mac OS X 用户的另一个注意事项:出于某种原因,-i 标志需要传递一个参数,即使它只是一个空字符串,例如 sed -i '' '/pattern/d' ./infile
P
Peter Mortensen

除了 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

如何删除带有模式的特定行以及紧接其上方的行?我对不同数据之间有数千条这样的行很满意。
在 OS/X 上,shell 变体不保留前导空格,但 grep -v 变体对我来说效果很好。
sed 示例具有不同的行为,它只是 greps!它应该类似于 sed -n -i '/pattern/!p' file
当每一行都与模式匹配时,grep 版本不起作用。更好的做法:grep -v "pattern" file > temp; mv temp file 这可能适用于其他一些示例,具体取决于返回值。
“打印反向比实际删除更快”-不在我的机器上(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。 (对于较小的文件,差异较大。)
D
DomainsFeatured

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

例如

sed -i '/pattern/d' filename      

或者

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

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


也投票赞成您的答案,只是因为您尝试了性能比较!
+1 用于提供使用 grep 行覆盖当前文件的选项。
第二个“grep”解决方案也更适合大文件
我很好奇如果是 sed '/pattern/d' filename > filename2; mv filename2 filename 会有什么性能差异
(使用 ubuntu 的 /usr/share/dict/words) grep 和 mv: 0.010s | sed 到位:0.197s | sed 和 mv:0.031s
c
codeforester

使用 GNU sed 的简单方法:

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

对于偶然发现此问答线程并且不熟悉 shell 脚本的其他人来说,这是一个方便的提示:短选项适合在命令行上一次性使用,但在脚本中应该首选长选项,因为它们更具可读性。
+1 为 --in-place 标志。我需要在受权限保护的文件上进行测试。 (必须做一些用户擦洗。)
请注意,长选项仅在 GNU sed 上可用。 Mac 和 BSD 用户需要安装 gsed 才能做到这一点。
另一个提示:如果您的正则表达式似乎不匹配,请尝试使用 -r 选项(或 -E,具体取决于您的版本)。这允许使用正则表达式元字符 +?{...}(...)
当您的磁盘没有更多空间并且您无法将文本复制到另一个文件时,这是正确的答案。这个命令做什么被质疑?
P
Peter Mortensen

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

ex +g/match/d -cwq file

在哪里:

执行给定的 Ex 命令(man ex),与执行 wq(写入和退出)的 -c 相同

g/match/d - Ex 命令删除具有给定匹配的行,请参阅:g 的幂

上面的示例是一种符合 POSIX 的方法,用于根据此 post at Unix.SEPOSIX specifications for ex 就地编辑文件。

sed 的区别在于:

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

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


太好了...当我执行 man ex 时,它给了我 vim 的人,似乎 ex 是 vim 的一部分...如果我理解正确,这意味着 match 的模式语法是 vimregex.com哪个与 POSIX 和 PCRE 风格相似但不同?
:g 是带有一些 slight differencesPOSIX-compliant 命令。我认为 PCRE 是基于它的。
@kenorb“I/O 开销和其他一些不良副作用”你能详细说明一下吗? AFAIK ex 正在使用临时文件,就像其他所有理智的工具一样,除了使用 dd 的 idk
P
Peter Mortensen

我在 Mac 上为此苦苦挣扎。另外,我需要使用变量替换来做到这一点。

所以我用:

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

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

我从这个 comment 中选择了 ''

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


Mac sed-i 之后需要一个参数,所以如果您不想备份,您仍然需要添加一个空字符串:-i ''
对于外壳使用 sed -i "/$pattern/d" $file 。谢谢您的回答。
P
Peter Mortensen

你也可以使用这个:

 grep -v 'pattern' filename

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


J
Jahid

要使用 grep 获得类似的结果,您可以执行以下操作:

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

这仅适用于 bash shell 或类似的(不是 tcsh)。
J
Jadzia

我用一个包含大约 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?
我使用的平台是 Linux (Gentoo)。 sed 版本是 GNU sed v 4.2.2,perl 版本是 perl 5(我不知道我在测试时使用的是哪个版本),而 grep(GNU)是 3.0 版本。
G
Giacomo1968

赛德:

'/詹姆斯\|约翰/d'

-n '/詹姆斯\|约翰/!p'

AWK:

'!/詹姆斯|约翰/'

/詹姆斯|约翰/ {下一个;} {打印}

GREP:

-v '詹姆斯\|约翰'


d
djperalta

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

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

K
Kjetil S.
perl -i    -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3

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

第二个命令做同样的事情,但通过将 .bk 添加到文件名来保留原始文件的副本或备份(.bk 可以更改为任何内容)。


G
GordyCA

您还可以删除文件中的一系列行。例如删除 SQL 文件中的存储过程。

sed '/CREATE PROCEDURE.*/,/END ;/d' sqllines.sql

这将删除 CREATE PROCEDURE 和 END ; 之间的所有行。

我已经用这个 sed 命令清理了许多 sql 文件。


S
Shizzmo

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


A
Andrey Izman
cat filename | grep -v "pattern" > filename.1
mv filename.1 filename

您正在覆盖仍在使用的文件。
@DavorCubranic 已修复
P
Peter Mortensen

以防万一有人想对字符串进行精确匹配,您可以在 grep - w 中使用 -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

有点误导。 -w, --word-regexp Select only those lines containing matches that form whole words.-x, --line-regexp Select only those matches that exactly match the whole line. For a regular expression pattern, this is like parenthesizing the pattern and then surrounding it with ^ and $.
n
nassim

在控制台中显示处理后的文本

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 将一次显示一页的文本。


p
peak

奇怪的是,接受的答案实际上并没有直接回答问题。该问题询问有关使用 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" 

与本页其他地方所述的变化。


S
Shawn

您可以使用旧的 ed 以与使用 exthe answer 类似的方式编辑文件。在这种情况下,最大的不同是 ed 通过标准输入获取命令,而不是像 ex 这样的命令行参数。在脚本中使用它时,适应这种情况的常用方法是使用 printf 将命令传递给它:

printf "%s\n" "g/pattern/d" w | ed -s filename

或使用heredoc:

ed -s filename <<EOF
g/pattern/d
w
EOF

N
NicolasElPapu

我发现大多数答案对我没有用,如果您使用 vim,我发现这非常简单明了:

:g/<pattern>/d

Source


如果您对 vim 而不是 sed 感到满意,您可以使用 ex stackoverflow.com/a/33186317 请注意它的速度较慢。 vim 的好处是您可以\v<pattern> 避免反斜杠