删除不包含特定字符串的文件

20
我想查找不包含特定字符串的文件(在一个目录及其子目录中),并删除这些文件。我该怎么做?

2
@SivaCharan 是的...从存在中 :) - Lev Levitsky
我所说的“remove”是指删除。 - Hakim
8个回答

16
以下方法可行:
find . -type f -print0 | xargs --null grep -Z -L 'my string' | xargs --null rm
这将首先使用find打印当前目录和任何子目录中所有文件的名称。这些名称将以null终止符而不是通常的换行符(尝试将输出导向od -c以查看-print0参数的效果)。
然后,xargs--null 参数告诉它接受以 null 终止的输入。然后,xargs 将在文件名列表上调用grepgrep 命令的 -Z 参数就像 find 命令的 -print0 参数一样工作,因此 grep 将以 null 终止方式打印其结果(这就是为什么最后一次调用 xargs 需要一个 --null 选项的原因)。grep 命令的 -L 参数会导致 grep 打印那些没有与正则表达式匹配的命令行中的文件名(这是由 xargs 添加的):
  

my string

如果您想进行简单匹配而不是使用正则表达式,请添加 -F 选项。如果您想使用更强大的正则表达式,则给出一个 -E 参数。使用单引号而不是双引号是个好习惯,因为这样可以保护您免受应用于字符串的任何 shell 魔法(例如变量替换)的影响。
最后,再次调用 xargs 去除前面找到的所有文件。
直接从 find 命令使用 -exec 参数调用 grep 的问题在于,对于每个文件,grep 都会被调用一次而不是针对整批文件一次像 xargs 那样。如果您有大量文件,则使用 xargs 要快得多。同时也不要试图做类似以下的事情:
rm $(some command that produces lots of filenames)

将文件名通过管道传递给xargs更好,因为它知道命令行的最大限制,并且会多次调用rm,每次尽可能地传递参数。

请注意,如果不需要处理包含空格和换行符的文件,则此解决方案将更简单。

或者

grep -r -L -Z 'my string' . | xargs --null rm

这也可以(而且更短)。grep 命令的 -r 参数会读取目录中的所有文件,并递归进入任何子目录。如果您还想对文件进行其他测试(如年龄或权限),请使用 find ... 方法。

请注意,任何带有单破折号引导符的单字母参数都可以组合在一起(例如作为 -rLZ)。但是请注意,find 不使用相同的约定,并具有用单破折号引入的多字母参数。这是由于历史原因而没有被修复,因为这样做会破坏太多脚本。


1
您可以使用grep的egrep形式,然后搜索替代字符串,如下所示:egrep ... 'Astring|Bstring|Cstring' ... - Nick
1
@user1394 你遇到了什么问题? - Nick
xargs: illegal option -- - - user1394
1
@user1394,你能给我展示一下你正在输入的内容以及你看到的响应吗? - Nick
@Nick 我正在使用 MacOS。 - user1394
显示剩余4条评论

5

编辑:以下是不应该这样做的方法!原因在这里。感谢@ormaaj指出!

find . -type f | grep -v "exclude string" | xargs rm

注意:grep模式将匹配当前目录下的完整文件路径(请参见find . -type f输出)。

1
踩踏并不是我,但是提醒一下,可能是由于xargs导致的。 - ormaaj
@ormaaj 哇!我以前经常用这个(xx;) 谢谢你指出来。 - rodion
这个命令真的很棒,它运行得非常快速和完美。 - Edwin Reyes
简而言之,为什么你不应该使用它? - rubo77

5

GNU grep和bash。

grep -rLZ "$str" . | while IFS= read -rd '' x; do rm "$x"; done

如果需要可移植性,请使用find解决方案。这样稍微快一点。


@Peter.O 奇怪,我无法重现这个问题。如果使用 -r 参数,它似乎只能默认为文件,除非显式地给出 - 参数。不过,加上 . 可能是一个好主意。我已经添加了它。 - ormaaj
当您添加了“.”时,我已经删除了我的原始评论,正如您无疑所见,但我也有点困惑,因为'man grep'显示最后一个选项为[FILE ...]...但这绝对是在这里发生的事情(即等待输入,没有.); GNU grep 2.5.4 - Peter.O
1
grep -r 的默认“.”是在 grep 2.11 中添加的,相当近期。而 GNU 并不太重视 man 手册。 - Alan Curry
啊,这里是 2.12。我存储库中提供的最老版本是 2.5.4 - ormaaj

4
一种可能性是:
find . -type f '!' -exec grep -q "my string" {} \; -exec echo rm {} \;

如果预览输出正确,您可以删除echo

使用-delete的等效方法是

find . -type f '!' -exec grep -q "user_id" {} \; -delete

但是这样就无法获得漂亮的预览选项了。


1
这对我不起作用。我可以看到rm命令在命令行中运行,但完成命令后没有任何内容被删除。所有文件仍然存在。我手动检查了文件,并发现其中一些不包含该字符串... - Hakim
似乎有些混淆,不确定您是要识别没有特定字符串的文件名还是文件内容。这个答案(使用-v而不是-q)将查看文件名。我的答案则查看文件内容。 - Alan Curry
@Hakim 命令回显而不是实际删除。在删除之前,__尝试__一下是一个好习惯。如果列出的文件正确,请删除单词 echo - Lev Levitsky
@TobySpeight 问 Ian,不要问我。如果有人询问某事,他期望得到答案,而不是完全相反的结果。当似乎工作正常时,删除那个回声并不难。你的行动是错误的,伙计。 - Flash Thunder
@Flash - 没有必要大动干戈;只需进行小修改即可更正答案,这样更容易一些;-) - Toby Speight
显示剩余2条评论

2
删除不包含特定字符串的文件:
Bash:
要使用它们,请按以下方式启用extglob shell选项:
shopt -s extglob

只需删除所有不包含字符串“fix”的文件:
rm !(*fix*)

如果您想不删除所有没有名称“fix”和“class”的文件:
rm !(*fix*|*class*)

Zsh:

要使用它们,请按以下方式启用扩展的 glob zsh shell 选项:

setopt extended_glob

删除所有没有字符串的文件,在本例中为“fix”:
rm -- ^*fix*

如果你想不删除所有文件名不是 "fix" 和 "class" 的文件:
rm -- ^(*fix*|*class*)

可以将其用于扩展,只需要更改正则表达式:(.zip),(.doc)等等。
以下是来源:

https://www.tecmint.com/delete-all-files-in-directory-except-one-few-file-extensions/

https://codeday.me/es/qa/20190819/1296122.html


这绝对是最简单的答案。我喜欢通配符语法,并且可能会将shopt命令添加到我的bashrc中...有什么缺点吗? - pretzlstyle

1

我可以想到几种方法来处理这个问题。这是其中一种:使用 find 和 grep 生成一个没有匹配项的文件列表,然后使用 xargs rm 删除它们。

find yourdir -type f -exec grep -F -L 'yourstring' '{}' + | xargs -d '\n' rm

这假设使用GNU工具(grep -L和xargs -d不可移植),当然也不含有文件名中带有换行符的情况。它的优点是不需要针对每个文件分别运行grep和rm,因此速度相当快。我建议在释放破坏之前,用“echo”代替“rm”进行测试,以确保选择了正确的文件。


3
这个也没有正确使用xargs。如果要使用xargs,必须加上-0参数。 - ormaaj
-d '\n' 是一个足够好的方法,直到你遇到文件名中有换行符的情况。它可以禁用大部分 xargs 的愚蠢行为,就像 -0 一样。不过了解 grep -Z 也是很好的。 - Alan Curry
1
一个带有换行符的文件名?那么键盘上有猫的那个人怎么办?他无法像这样输入长命令!哎呀,太疯狂了。但你可以在YouTube上找到它。 - J-16 SDiZ
没问题,只要文件名合理就可以。 - ormaaj
好的。这对我来说很有效。我删除了文件名中的空格并检查了这个脚本。谢谢Alan。 - Hakim

0
另一种解决方案(虽然不够快)。顶部的解决方案在我的情况下不起作用,因为我需要用来替换“我的字符串”的字符串具有特殊字符。
find -type f ! -name "*my string*" -exec rm {} \; -print

0

这个对我有用,如果你能接受删除目录,你可以删除 -f。

myString="keepThis"
for x in `find  ./`
    do if [[ -f $x && ! $x =~ $myString ]]
        then rm $x
    fi
done

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