有一个文本文件 ,del.txt
包含随机生成的标识字符串列表,例如:
07ckv978yk0
HuinLBoZHcY
_oR7ccXiunY
我想删除文件名包含任何识别字符串的所有文件。文件名的格式为20191223_abcdef_07ckv978yk0.json
,其中匹配的字符串肯定会在文件名的末尾找到,就在文件扩展名之前,文件扩展名不一定是 .json。也可能有多个文件与每个字符串匹配,但绝不会有多个字符串与一个文件匹配。
我尝试过类似的方法find . -type f -name "*" -exec grep -Flf ~/del.txt '{}' \; -print
,虽然它似乎有效,但需要非常很长时间(有 2000 个字符串需要匹配,甚至更多的文件名需要检查)。我可以通过指定扩展名来加快速度,但有很多不同的文件扩展名,我希望有一种更智能的方法来做到这一点。
答案1
您编写 find 命令的方式是:
grep
对每个文件名运行一次- 正在摸索内容每个文件,而不是文件姓名本身。
这将非常缓慢,而且不会做你真正想做的事。
尝试更多类似这样的事情:
find . -type f | grep -Ff ~/del.txt
这只会运行 grep 一次,并且会 grep 文件名,而不是文件内容。
如果要同时删除匹配的文件,而不是仅仅生成要删除的文件列表:
find . -type f -print0 | grep -z -Ff ~/del.txt | xargs -0r rm
它使用 NUL 作为文件名之间的分隔符(这是唯一不能出现在路径/文件名中的字符),因此可以安全地与任何文件名一起使用,即使是包含空格、换行符和 shell 元字符的文件名。
del.txt
上述两个版本都将匹配文件名中出现的固定字符串。如果您只想在文件名中的特定位置进行匹配,则必须使用正则表达式而不是固定字符串。或者也许使用比 grep 更强大/更灵活的东西来进行匹配(例如awk
或perl
,两者在处理 NUL 分隔输入时都没有问题 - 并且 perl 也有一个内置unlink()
函数来删除文件,因此不需要xargs
)。
如果您只是想要一些简单的东西,例如仅在任何文件名“扩展名”(文字字符)之前匹配的模式.
,那么您可以进行修改del.txt
,以便每个固定字符串以.
您可以手动编辑文件,或者可以使用流程替代
并sed
即时修改它。例如:
find . -type f -print0 |
grep -z -Ff <(sed -E -e 's/([^.])$/\1./' del.txt) |
xargs -0r rm
该sed
命令将 a 添加.
到尚未以 a 结尾的每一行.
- 请注意,它不会修改del.txt
自身,它只是修改流副本,然后由 grep 的选项使用-f
。
答案2
在zsh
:
blacklist=( ${(f)"$(<del.txt)"} )
print -rC1 -- **/*(${(~j[|])blacklist}).*(D.)
将它们打印r
在1
C
柱上。要删除它们,请替换print -rC1
为rm -f
.
或者没有临时数组:
print -rC1 -- **/*(${(~j[|])${(f)"$(<del.txt)"}}).*(D.)
"$(<del.txt)"
扩展为文件内容减去尾随换行符,如 ksh 中- 参数
f
扩展标志按行f
eed 字符( 的缩写)进行分割ps[\n]
,因此$blacklist
是一个数组,每个元素都是 中非空行的内容del.txt
。 j[|]
用 来连接元素|
。对于~
,它|
被视为全局运算符而不是字面意思。所以我们最终得到了一个**/*(07ckv978yk0|HuinLBoZHcY|_oR7ccXiunY).*
全局模式。**/
任何级别的子目录。D
:还包括D
otfiles(隐藏文件).
: 限制为常规的文件
答案3
如果没有数千个文件要删除:
rm `sed 's/^/*/;s/$/.*/' del.txt`
这将获取文件的每一行,将其转换为sed到像这样的全局模式*idstring.*
并将其放在命令行上R M去除。
如果 中的 id 字符串太多del.txt
,则命令行可能太长,shell 无法接受。
要查看首先删除的内容,请替换R M和LS。
注意:这不适用于桀骜我的设置方式(你需要一个评估),但它适用于巴什和嘘。它甚至可以处理带有空格和其他 shell 元字符的文件名,我承认这让我感到惊讶。
如果任何具有匹配 id 字符串的文件已被删除,则会出现错误消息。在删除命令后使用rm -f
替代,或重定向2>/dev/null
,或删除。del.txt