我正在寻找一个命令或 bash 脚本来删除所有文件夹,除非它们在第一级子文件夹中具有特定文件类型 (*.pdf)。
folder01
a.txt
y.txt
folder02
b.pdf
z.txt
folder03
h.txt
folder03.1
c.pdf
在上面的例子中folder01
,folder03
需要删除。
我的尝试:
#!/bin/bash
shopt -s globstar
# Loop through every subdirectory.
for d in **/; do
f=("$d"/*)
if [[ -f "$f" && ! "${f##*/}" =~ ^*.pdf$ ]]; then
# `echo` to ensure a test run; remove when verified.
echo rm -r -- "$d"
fi
done
答案1
这似乎运行良好(编辑:仅当它有一个 pdf 文件时):
for d in */; do
if ! [ -f $d/*.pdf ]; then
echo "Will remove $d"
fi
done
(-f
在指定路径下查找文件;-e
一般会查找某物在那条路上)
编辑:为了考虑带有空格的路径和单个目录中的多个 PDF 文件,您可能需要使用find
,例如:
for d in */; do
if [[ -z $(find "$d" -maxdepth 1 -name "*.pdf" -type f) ]]; then
echo "Will remove $d"
fi
done
我将其从 改为**/
因为*/
对于您的用例,我相信您会这样做不是想要globstar
和**/
- 这些将使其循环遍历子目录,例如:
> for d in **/; do echo $d; done
folder01/
folder02/
folder03/
folder03/folder03.1/
在测试用例中,这似乎不会改变最终结果,但如果您只对.pdf
第一级子目录感兴趣,则不需要循环遍历任何子目录。
如果您想要删除任何级别都没有 pdf 的目录,您可以将 if 语句更改为:
if ! [ -f $d/**/*.pdf ]; then
-maxdepth 1
编辑:或从命令中删除find
。
答案2
以下命令打印即将被删除的目录的路径名:
# cd to the right directory first
find . -type d ! -name . \( -exec [ -r {} ] \; -o ! -prune \) \
-exec sh -c '
set -- "$1"/*.pdf
! [ -e "$1" ]
' find-sh {} \; -prune -print
如果结果看起来正确,则-exec rm -r {} +
在 之后附加-print
。即使您的find
支持-delete
,不要使用它,因为它不能删除非空目录。
该代码通过为每个考虑的目录运行一个 shell 来工作。shell 使用通配符来检测*.pdf
目录中匹配的文件。几点说明:
-prune
末尾附近可防止进入无论如何都会被删除的目录。例如,在我们有资格删除后find
检查是没有意义的。要明确的是:删除意味着删除,即使中有匹配的文件。./folder03/folder03.1
./folder03
./folder03
rm -r
./folder03/folder03.1
*.pdf
folder03.1
! -name .
如果起始路径是 ,则是-mindepth 1
GNU 的POSIX 等效版本(不可移植)。如果起始路径是 ,则可移植地执行此操作很容易,否则就不那么容易了。因此我设计了解决方案,因此您需要事先进入正确的目录。find
.
.
cd
*.pdf
不匹配隐藏文件(点文件)。您的尝试也使用了通配符,因此我想这对您来说没问题。*.pdf
区分大小写。不区分大小写的模式是*.[pP][dD][fF]
。*.pdf
火柴文件任何类型的文件,不一定是常规文件。它只是名字。在您的一条评论中,您写道“*.pdf
作为主要规则,它是安全的”。就这样吧。如果没有匹配的文件,
*.pdf
则在 POSIX shell 中保留其文字形式;因此至少有一个“匹配”,我们不知道它是否匹配。在具有更多功能的 shell 中(例如在 Bash 中),您可以对此采取一些措施,但我希望我的代码具有可移植性。这就是为什么我测试文件系统中是否存在第一个“匹配”(! [ -e "$1" ]
)而不是依赖匹配的数量。您不需要能够
cd
访问正在测试的每个目录。如果您无权读取目录,则 shell 代码将无法
*.pdf
在其中找到任何内容(即使此类文件确实存在)。尝试访问rm -r
目录将失败(除非目录已经为空),将生成一些错误消息。-exec [ -r {} ] \; -o ! -prune
阻止find
尝试读取此类目录的内容并尝试对其进行测试、删除它。如果不允许读取的目录是一个问题,您可能需要根据需要调整解决方案的这一部分。find-sh
解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh
?
答案3
假设特定文件类型为 *.pdf >
将您不想删除的包含 *.pdf 的目录放入文件删除中
find -name *.pdf -exec dirname {} ';' > temp && sed 's/\.\///g' temp| sed 's![^/]$!&/!'> remove.txt
将当前路径下的所有目录放入文件 current.txt
ls -d */>current.txt
比较 current.txt 和 remove.txt 并从 current.txt 中删除不在 remove.txt 中的文件
comm -23 <(sort current.txt) <(sort remove.txt)|sed 's/^/"/g' | sed 's/$/"/g' | xargs rm -r
&& rm current.txt remove.txt
注意:如果您只想要带有 *.pdf 的目录,则可以附加或删除当前路径中的所有文件和目录ls> current.txt
,然后改为使用第二步。这将删除在过程中已存在和创建的所有“文件”