除了两个特定的目录外,删除所有文件/目录

5

看起来有一些问题要求删除符合某些情况的文件/目录,但我正在寻找完全相反的东西:删除文件夹中与我的提供的示例不匹配的所有内容。

例如,这是一个示例目录树:

.
|-- coke
|   |-- diet
|   |-- regular
|   `-- vanilla
|-- icecream
|   |-- chocolate
|   |-- cookiedough
|   |-- cupcake
|   |   |-- file1.txt
|   |   |-- file2.txt
|   |   |-- file3.txt
|   |   |-- file4.txt
|   |   `-- file5.txt
|   `-- vanilla
|-- lol.txt
|-- mtndew
|   |-- classic
|   |-- codered
|   |-- livewire
|   |   |-- file1.txt
|   |   |-- file2.txt
|   |   |-- file3.txt
|   |   |-- file4.txt
|   |   `-- file5.txt
|   `-- throwback
`-- pepsi
    |-- blue
    |-- classic
    |-- diet
    `-- throwback

我想删除除了test/icecream/cupcake/和test/mtndew/livewire/文件夹中的文件之外的所有内容,包括目录结构。那么,我该如何实现呢?我希望这个操作可以用bash或python语言实现。
10个回答

6
此命令将仅在其原始目录中保留所需的文件:
find test \( ! -path "test/mtndew/livewire/*" ! -path "test/icecream/cupcake/*" \) -delete

不需要使用cpio。它在Ubuntu、Debian 5和Mac OS X上都可以工作。

在Linux上,它会报告无法删除非空目录,这正是期望的结果。在Mac OS X上,它会静默地完成正确的操作。


这会找到测试并将其删除,从而删除您试图保存的目录。 - Dennis Williamson
这会找到测试并且不删除它,因为它是非空的。是否有任何现代Linux或Unix的版本,该命令无法正常工作? - Patrick Webster
抱歉,在我的系统中设置测试时出了一些错误。你的例子对我来说是正确的。 - Dennis Williamson
虽然这不是最初的问题,但有些人可能会发现这很有用:您可以通过在“-delete”之前添加“-type f”标志来保留整个目录结构并仅删除未使用“test”排除的文件。 - kevinmicke

4

find-prune 可以考虑使用,但是让它针对特定路径(icecream/cupcake/)而不是特定目录(cupcake/)工作很麻烦。

个人建议只需使用 cpio 并将要保留的目录中的文件进行硬链接(避免复制),然后将其移动到新目录树中并删除旧目录树即可:

find test -path 'test/icecream/cupcake/*' -o -path 'test/mtndew/livewire/*' | cpio -padluv test-keep
rm -rf test

那样还会保持您想要保留的目录的现有结构。

3

除了“except”之外的所有内容都是我们需要if语句的原因;也是os.walk目录列表可变的原因。

for path, dirs, files in os.walk( 'root' ):
    if 'coke' in dirs:
        dirs.remove('coke')
        dirs.remove('pepsi')

2
find /path/to/test/ -depth -mindepth 1 \
! -path "/path/to/test/icecream/cupcake/*" \
! -path "/path/to/test/icecream/cupcake" \
! -path "/path/to/test/icecream" \
! -path "/path/to/test/mtndew/livewire/*" \
! -path "/path/to/test/mtndew/livewire" \
! -path "/path/to/test/mtndew"
 -delete -print

写下所有路径可能有点繁琐,但这是使用find命令的唯一方式。


2
您可以基于Python的os.walk函数进行一些操作:
import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))

“...只需添加一些内容以忽略您感兴趣的路径。”

2

将您想保留的内容移动到其他地方,然后删除剩下的内容。


0

使用find命令。

你的命令应该长这样:

find $directory \( -prune 'some pattern' \) -delete

尝试了一下,它显示如下内容:find /home/phuzion/test/ \( -prune 'icecream/cupcake/' \) -delete find: 路径必须在表达式之前 - phuzion
从“find”命令的man页面中得知:“因为-delete意味着-depth,所以不能同时使用-prune和-delete。” - Dennis Williamson
正确的语法更像是“find $directory -path ./pepsi/diet -prune -o -exec some-command '{}' ;”,这样可以消除错误信息。 - Dennis Williamson

0
一行代码解决问题: find . |grep -v "test/icecream/cupcake/"| grep -v "test/mtndew/livewire/"|xargs rm -r 因为不起作用,所以已删除。
如果文件名中有空格,可能会让你陷入麻烦,并且如果有其他匹配模式的树,则可能会保留更多文件。
一个比较好的解决方案:
find . |sed "s/.*/rm '&'/"|grep -v "rm './test/icecream/cupcake/"| grep -v "rm './test/mtndew/livewire/"|sh

没有实际测试过,如果出了问题,你可以保留两个部分。

编辑:正如Dennis所指出的那样,它不仅会分裂成两个部分 :-) 已更正第二个示例中的拼写错误并删除了第一个示例


在你的第一个例子中,它将删除 test/icecream 目录,其中包括杯子蛋糕(例如)。因此,即使你已经使用 grep -v 排除了杯子蛋糕,它们仍然会被“吃掉”。第二个例子有几个拼写错误和不平衡的单引号。sed 后面应该有一个空格,零应该是一个和号。 - Dennis Williamson
你仍然在路径周围使用不平衡的单引号。例如,应该是“rm './test/icecream/cupcake/'”。 - Dennis Williamson

0

对我来说,使用两个步骤的查找功能可以解决问题:首先删除允许的文件,然后再删除它们的空目录!

find -x -E ~/Desktop/test -not \( -type d -regex '.*/(cupcake|livewire)/*.*' -prune \) -print0 | xargs -0 ls -1 -dG 

# delete the files first

# Mac OS X 10.4 
find -x -E ~/Desktop/test -not \( -type d -regex '.*/(cupcake|livewire)/*.*' -prune \) -type f -exec /bin/rm -fv '{}' \; 

# Mac OS X 10.5 
find -x -E ~/Desktop/test -not \( -type d -regex '.*/(cupcake|livewire)/*.*' -prune \) -type f -exec /bin/rm -fv '{}' + 

# delete empty directories 
find -x ~/Desktop/test -type d -empty -delete 

在Ubuntu上,find命令没有-x -E选项,因此等效的命令应该是:find ~/Desktop/test -xdev -regextype posix-extended-not ( -type d -regex './(cupcake|livewire)/.*' -prune ) -print0 | xargs -0 ls -1 -dG - Dennis Williamson
此外,在某些情况下,您可能需要保护空目录,以便在最后一步中使用与您答案中的find -empty命令相同的正则表达式来保留这些目录。您可能需要澄清您的第一个命令是概念验证测试,并且不是您演示的两个步骤的一部分。 - Dennis Williamson

0
像其他人一样,我使用了os.walk和os.path.join来构建要删除的文件列表,使用fnmatch.fnmatch来选择必须包含或排除的文件。
#-------------------------------#
# make list of files to display #
#-------------------------------#
displayList = []
for imageDir in args :
    for root,dirs,files in  os.walk(imageDir) :
        for filename in files :
            pathname = os.path.join( root, filename ) 
            if fnmatch.fnmatch( pathname, options.includePattern ) :
                displayList.append( pathname )


#----# now filter out excluded patterns #----#
try :
    if len(options.excludePattern) > 0 :
        for pattern in options.excludePattern :
            displayList = [pathname for pathname in displayList if not fnmatch.fnmatch( pathname, pattern ) ]
except ( AttributeError, TypeError ) :
    pass

如果fnmatch不够用,您可以使用re模块来测试模式。
在我处理文件之前,我已经构建了文件列表,但是您也可以在生成文件时处理它们。
try/except块……是为了防止我的选项类实例没有排除模式,或者如果它是错误类型,则在fnmatch中引发异常。
这种方法的局限性在于它首先包括与模式匹配的文件,然后再排除。如果您需要比这更灵活的功能(包括匹配模式a,但不包括模式b,除非模式c……),那么上面的片段就不够用了。事实上,在完成此练习时,您开始看到find命令语法的方式。似乎笨重,但实际上这正是做这件事的方法。
但是,如果您生成一个列表,就可以根据需要过滤它,以符合任何包含/排除规则。
生成列表的一个好处是在执行删除操作之前可以先检查列表。这有点像“--dryrun”选项。您可以在Python解释器中交互式地执行此操作,打印列表以查看其外观,应用下一个过滤器,查看它是否删除了太多或太少等等。

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