删除文件,但排除列表中的所有文件


17

我需要定期清理文件夹。我得到一个包含文本的文件列表,允许哪些文件。现在,我必须删除该文件中没有的所有文件。

例:

dont-delete.txt

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

我的文件夹执行清理包含以下示例:

ls /home/me/myfolder2tocleanup/

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

因此,应删除以下文件:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

我搜索一些东西来创建删除命令,并带有一个选项来排除文件提供的某些文件。


这是家庭作业吗?
mook765

我希望你不是他的老师。大声笑
古吉拉特邦桑塔纳

2
@gujarat我们不是免费的家庭作业服务,因此此评论是合理的。至于问题本身,它可能对其他人有用,因此目前为止尚待解决。
Sergiy Kolodyazhnyy

@Serg我完全同意您的观点
Gujarat

Answers:


9

rm命令已被注释掉,以便您可以根据需要检查并验证其是否正常运行。然后,只需取消注释该行。

check directory节将确保您不会意外从错误的目录运行脚本并破坏错误的文件。

您可以删除该echo deleting行以使其静默运行。

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done

我编辑了您的代码,以避免无用的使用ls和对输出的无用捕获(grep如果您只想知道是否存在匹配项)。我还使用固定字符串模式来避免转义问题。
David Foerster

@DavidFoerster感谢您的贡献。但是,当你改变了while循环的for循环中,您在不经意间改变了iteration keyif。在声明中,它破坏了代码。我修好了它。
LD詹姆斯

糟糕,习惯的力量。我倾向于将shell变量名称缩写为f。;-P(…和+1,我早些时候忘记了。)
David Foerster,2016年

10

这个python脚本可以做到这一点:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

重要的部分是取消注释os.unlink()功能。

注意:将此脚本和添加dont-delete.txt到您的脚本中,dont-delete.txt以便它们都在列表中,并将它们保存在同一目录中。


1
我将您的代码更改set为在第二部分中使用O而不是O(1)列表而不是O(n)查找。
David Foerster

感谢您的帮助,我通常是Windows专家,但是python接缝也很酷=)
stefan83 '16

1
@ stefan83:Python在Windows上也能很好地运行。
David Foerster

3

这里是单线:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls 打印当前目录中的所有文件(按排序顺序)
  2. sort dont_delete 按排序顺序打印我们不想删除的所有文件
  3. <()操作者接通一个字符串转换为一个类文件对象
  4. comm命令比较两个预先排序的文件,并打印出它们不同的行
  5. 使用这些-2 -3标志将导致comm仅打印第一个文件中包含的行,而不打印第二个文件中的行,这将是可以安全删除的文件列表
  6. tail +2呼叫只是删除的标题comm输出,其中包含输入文件名
  7. 现在,我们获得了标准输出时要删除的文件列表。我们将此输出通过管道xargs传输到,它将输出流转换为的参数列表rm。该-p选项强制xargs执行之前要求确认。

谢谢您的帮助,现在我有了解决方案!
stefan83 '16

@gardenhead,我对您的代码感到厌倦,但是它删除了目录中的所有文件,并且仅将第一个和最后一个文件保留在dont-delete列表中。您对这个问题有什么想法吗?提前致谢。
内加尔

1

FWIW看起来您可以zsh使用(+cmd)glob限定符在本地进行此操作。

为了说明,让我们从一些文件开始

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

和白名单文件

 % cat keepfiles.txt
foo
kazoo
bar

首先,将白名单读入数组:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

也许更好

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(相当于bash的mapfile内建-或其同义词readarray)。现在我们可以检查数组中是否存在键(文件名),${keepfiles[(I)filename]}如果找不到匹配项,则使用键返回0:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

我们可以使用它来创建一个函数,true如果$REPLY数组中没有匹配项,则返回该函数:

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

最后,我们在命令中将此函数用作限定符:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

或者,就您而言

 % rm -- *(+nokeep)

(您可能希望将白名单文件本身的名称添加到白名单中。)


0

假设您的bash shell已extglob shopt启用,则这是一个较为保守的选择:

rm !($(tr \\n \| < keep.txt))

(...伴随@gardenhead的其他极好的交流建议!)


0

除非的输出ls /home/me/myfolder2tocleanup/超出最大的shell参数限制 ARG_MAX(对于Ubuntu而言,最大2MB左右),否则我将建议以下内容。


可以完成此任务的单行命令实现如下:

  1. dont-delete.txt文件复制到包含要删除的文件的目录中,如下所示:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd 到包含要删除文件的目录,如下所示:
cd /home/me/myfolder2tocleanup/
  1. 进行试运行以测试该命令,并使其打印检测到要删除的文件的名称,而无需实际删除它们,如下所示:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. 如果您对输出感到满意,请通过运行以下命令删除文件:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

说明:

  • ls -p将列出所有文件和目录当前目录,并且该选项-p会将a添加/到目录名称中。
  • grep -v /通过删除/名称中包含的所有项目来排除目录。
  • sed 's/\<dont-delete.txt\>//g'将排除 dont-delete.txt文件,因此不会在该过程中将其删除。
  • sort 为了确保将剩余的输出排序 ls
  • comm -3 - <(sort dont-delete.txt) 将排序 dont-delete.txt文件进行,将其与的排序输出进行比较,ls并排除两者中都存在的文件名。
  • xargs rm将删除已处理的输出中的所有剩余文件名ls。这意味着所有在当前目录中的项目将除了被删除目录中列出的文件dont-delete.txt档案,并dont-delete.txt文件本身

在空运行部分:

  • xargs echo 将打印应删除的文件。
  • tr " " "\n" 会将空格转换为新的行,以便于阅读。

0

我强烈建议使用rsync这里发布的解决方案;否则使用以下提到的特殊情况的解决方案。

假设在名为的文件中列出的文件中没有空格(空格/制表符)excludelist,那么您将执行以下操作:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \)

只需添加-delete到上面的命令以删除excludelist文件中不存在的文件即可。如果您发现没有-delete选项,您可以使用rm-exec如下:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} \;

或者使用-exec+终止代替。

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} +

echo 仅用于空运行。


-1

我的建议是:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

更新2018-08-07

例:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

请注意,在第3行之后,您将拥有dont-delete.txt包含内容的文件:

./a
./b
./dont-delete.txt

(领先./非常重要的

文件cd将被删除。


我用一个用换行符分隔的文件名的文本文件尝试了此操作。最终删除了目录中的所有文件。
雅克·马拉帕德

我想您的“保留清单”是错误的。
nyxz

我添加了示例用法。
nyxz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.