如何使用 sed 删除文本文件中包含特定字符串的所有行?
要删除该行并将输出打印到标准输出:
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
示例具有不同的行为,它只是 greps!它应该类似于 sed -n -i '/pattern/!p' file
。
grep -v "pattern" file > temp; mv temp file
这可能适用于其他一些示例,具体取决于返回值。
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。 (对于较小的文件,差异较大。)
您可以使用 sed 替换文件中的行。但是,它似乎比使用 grep 反转到第二个文件然后将第二个文件移到原始文件上要慢得多。
例如
sed -i '/pattern/d' filename
或者
grep -v "pattern" filename > filename2; mv filename2 filename
无论如何,第一个命令在我的机器上花费了 3 倍的时间。
sed '/pattern/d' filename > filename2; mv filename2 filename
会有什么性能差异
使用 GNU sed
的简单方法:
sed --in-place '/some string here/d' yourfile
-r
选项(或 -E
,具体取决于您的版本)。这允许使用正则表达式元字符 +
、?
、{...}
和 (...)
。
您可以考虑使用 ex
(这是一个标准的基于命令的 Unix 编辑器):
ex +g/match/d -cwq file
在哪里:
执行给定的 Ex 命令(man ex),与执行 wq(写入和退出)的 -c 相同
g/match/d - Ex 命令删除具有给定匹配的行,请参阅:g 的幂
上面的示例是一种符合 POSIX 的方法,用于根据此 post at Unix.SE 和 POSIX specifications for ex
就地编辑文件。
与 sed
的区别在于:
sed 是流编辑器,而不是文件编辑器。 BashFAQ
除非您喜欢不可移植的代码、I/O 开销和其他一些不好的副作用。所以基本上一些参数(例如in-place/-i
)是非标准的FreeBSD扩展,可能在其他操作系统上不可用。
man ex
时,它给了我 vim
的人,似乎 ex
是 vim 的一部分...如果我理解正确,这意味着 match
的模式语法是 vimregex.com哪个与 POSIX 和 PCRE 风格相似但不同?
ex
正在使用临时文件,就像其他所有理智的工具一样,除了使用 dd
的 idk
我在 Mac 上为此苦苦挣扎。另外,我需要使用变量替换来做到这一点。
所以我用:
sed -i '' "/$pattern/d" $file
其中 $file
是需要删除的文件,$pattern
是匹配删除的模式。
我从这个 comment 中选择了 ''
。
这里要注意的是在 "/$pattern/d"
中使用 双引号。当我们使用单引号时,变量将不起作用。
sed
在 -i
之后需要一个参数,所以如果您不想备份,您仍然需要添加一个空字符串:-i ''
sed -i "/$pattern/d" $file
。谢谢您的回答。
你也可以使用这个:
grep -v 'pattern' filename
这里的 -v
将只打印您的模式以外的内容(这意味着反转匹配)。
要使用 grep
获得类似的结果,您可以执行以下操作:
echo "$(grep -v "pattern" filename)" >filename
bash
shell 或类似的(不是 tcsh
)。
我用一个包含大约 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
赛德:
'/詹姆斯\|约翰/d'
-n '/詹姆斯\|约翰/!p'
AWK:
'!/詹姆斯|约翰/'
/詹姆斯|约翰/ {下一个;} {打印}
GREP:
-v '詹姆斯\|约翰'
从所有匹配的文件中删除行
grep -rl 'text_to_search' . | xargs sed -i '/text_to_search/d'
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 文件。
echo -e "/thing_to_delete\ndd\033:x\n" | vim file_to_edit.txt
cat filename | grep -v "pattern" > filename.1
mv filename.1 filename
以防万一有人想对字符串进行精确匹配,您可以在 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 $.
在控制台中显示处理后的文本
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
将一次显示一页的文本。
奇怪的是,接受的答案实际上并没有直接回答问题。该问题询问有关使用 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"
与本页其他地方所述的变化。
您可以使用旧的 ed
以与使用 ex
的 the answer 类似的方式编辑文件。在这种情况下,最大的不同是 ed
通过标准输入获取命令,而不是像 ex
这样的命令行参数。在脚本中使用它时,适应这种情况的常用方法是使用 printf
将命令传递给它:
printf "%s\n" "g/pattern/d" w | ed -s filename
或使用heredoc:
ed -s filename <<EOF
g/pattern/d
w
EOF
我发现大多数答案对我没有用,如果您使用 vim,我发现这非常简单明了:
:g/<pattern>/d
不定期副业成功案例分享
sed '/pattern to match/d' ./infile > ./newfile
的新文件,或者如果您想进行就地编辑,则可以将-i
标志添加到 sed 中,如sed -i '/pattern to match/d' ./infile
.请注意,-i
标志需要 GNU sed 并且不可移植sed -i.backup '/pattern to match/d' ./infile
)这让我了解了就地编辑。sed
之类的命令应用于任何不受版本控制的文件。sed -i '' '/pattern/d' ./infile
。