是否可以用一行完成以下操作?
shopt -s extglob
rm -r !(folder1|file1|folder2)
rm -r folder2/!(subfolder)
rm -r folder2/subfolder/!(onefile)
例如,像这样的事情?
rm -r !(folder1|file1|folder2/subfolder/onefile)
答案1
您可以使用-path
该工具的(否定)测试find
。如果该工具支持-delete
,那么它相对简单:
find . ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile -delete
如果onefile
存在subfolder
则会folder2
报错cannot delete: Directory not empty
。这将使他们生存,但退出状态find
不会0
。
如果您find
支持,-empty
那么您可以在目录之前测试目录是否为空-delete
。一般来说,您不想测试非目录是否为空。这使命令变得复杂,但0
如果一切按计划进行,则允许命令返回。像这样:
find . ! -path ./folder1 \
! -path './folder1/*' \
! -path ./file1 \
! -path ./folder2/subfolder/onefile \
\( ! -type d -o -empty \) \
-delete
find
有足够的钱来支持-delete
并且-empty
可能会支持-regex
。我的 Debian 中的GNUfind
可以做到这一点:
find . ! -regex '\./folder1/*.*\|\./file1\|\./folder2/subfolder/onefile' -delete
笔记:
onefile
这是阻止find
删除subfolder
或的存在folder2
。您需要更多模式(见下文)或更复杂的正则表达式来保留这些目录,无论是否onefile
存在。具有更复杂正则表达式的变体:find . ! -regex '\./folder1/*.*\|\./file1\|\./folder2\(/subfolder\(/onefile\)?\)?' -delete
作为奖励,这种改进解决了“非零退出状态”问题。
-path
使用类似 glob 的模式匹配表示法(?
匹配单个字符并*
匹配零个或多个字符);-regex
使用正则表达式(.
匹配单个字符并.*
匹配零个或多个字符)。-path
并-regex
匹配文件的整个路径。要设计有效的模式,您需要知道find
将使用什么路径。提示:find
对于任何不是起点的路径,有义务不使用尾部斜杠。这就是我在 ( ) 中-path
为./folder1
和 文件使用单独模式的原因./folder1/*
。- 另一方面,起点按规定使用。如果起点是 ,那么除了自身的路径之外,
.
所有路径都将以 开头,那么它将只是。如果起点是则所有路径都将以 开头。./
.
.
./
./
在我的测试中,
-delete
静默处理.
,不会删除,也不会出现错误,因此无需排除.
.另一方面,它会抛出一个错误./
。我不确定这种行为有多可靠。如果有任何疑问,请明确排除.
,特别是如果您正在寻求有意义的退出状态。正则表达式类型很少。例如我
-regextype posix-extended
之前可以使用! -regex
.以下是我想出的最紧凑的命令(虽然不可移植):find . -regextype posix-extended \ ! -regex './folder1(/.*)?|./file1|./folder2(/subfolder(/onefile)?)?' \ -delete
我故意使用
.
(通配符)而不是\.
(文字点)作为前导.
.由于起点是.
,因此每条路径都.
肯定以 开头,因此通配符除了 之外不会匹配任何其他内容.
。\.
如果您更喜欢形式上的正确性,请使用。如果您想确保不会删除太多内容,可以“试运行”该命令。更改
-delete
为-print
,您将看到要删除的内容。这是第一个这样修改的命令:find . ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile -print
或者否定所有先前的测试作为一个整体(这与单独否定它们不同,除非您知道还需要更改什么;比较这到这)并更改
-delete
为-print
看看什么会幸存:find . ! \( ! -path ./folder1 ! -path './folder1/*' ! -path ./file1 ! -path ./folder2/subfolder/onefile \) -print
不加引号
./folder1/*
将是一个错误:shell 会尝试扩展*
.
-path
是POSIX 要求但是-delete
和-regex
不是-empty
。有时-exec rm {} +
可以使用 代替-delete
,但这样rm
不会删除目录,您需要rmdir
.是的,rm -r
可以删除目录,但随后rm -r ./folder2
也会删除./folder2/subfolder/onefile
并违背整个要点。
-delete
我们的基本方法仍然可以在没有(并且有所改进)的情况下实现:
find . -depth \
! -path . \
! -path ./folder1 \
! -path './folder1/*' \
! -path ./file1 \
! -path ./folder2 \
! -path ./folder2/subfolder \
! -path ./folder2/subfolder/onefile \
\( \( ! -type d -exec rm {} + \) -o \( -type d -exec rmdir {} + \) \)
通过显式排除./folder2
,./folder2/subfolder
我使命令返回有意义的退出状态并保留这些目录,即使这些目录onefile
不存在。
如果您想删除onefile
不存在的目录,则不要使用额外的模式。研究--ignore-fail-on-non-empty
的选项rmdir
。 POSIX 不需要此选项。
我不会说最后一个命令很紧凑。我喜欢那个-regex
更好的。
答案2
对于这样一个潜在的破坏性操作,我会手动小心地完成:获取所有文件的列表,以及完整路径;编辑掉应该保留的文件(grep -v
,或使用您最喜欢的文本编辑器);检查是否有任何遗漏;将列表提供给(如果列表很大,rm
可能需要使用)。xargs(1)
请小心包含空格或其他不寻常字符的文件名!