BASH:如何删除除清单中命名的文件以外的所有文件?

19

我有一个清单文件,它只是一个以换行符分隔的文件名列表。如何从文件夹中删除所有未在清单中命名的文件?

我尝试动态构建find ./ ! -name "filename"命令:

command="find ./ ! -name \"MANIFEST\" "
for line in `cat MANIFEST`; do
    command=${command}"! -name \"${line}\" " 
done
command=${command} -exec echo {} \;
$command

但文件仍然存在。

[注:] 我知道这里使用了echo。我想在使用它之前检查我的命令做了什么。

解决方案:(感谢PixelBeat)

ls -1 > ALLFILES
sort MANIFEST MANIFEST ALLFILES | uniq -u | xargs rm

无需临时文件:

ls -1 | sort MANIFEST MANIFEST - | uniq -u | xargs rm

两者都会忽略文件是否已排序。


谢谢大家!我把它交给 pixelbeat,因为他的 shell 指南提供了解决方案。 - brice
5个回答

15

在当前目录中,对于每个文件,在MANIFEST文件中使用grep查找文件名,如果不匹配则执行rmfile

for file in *
  do grep -q -F "$file" PATH_TO_YOUR_MANIFIST ||  rm "$file" 
done

1
如果您使用此方法,请确保将清单文件的名称添加到清单本身中,以防您将其放在删除文件的目录中。如果您将此代码放入脚本中,请还要将该脚本的名称添加到清单中。 - Ciske

14

使用来自http://www.pixelbeat.org/cmdline.html#sets的“集合差异”模式。

(find ./ -type f -printf "%P\n"; cat MANIFEST MANIFEST; echo MANIFEST) |
  sort | uniq -u | xargs -r rm

请注意,我将MANIFEST列在两次,以防那里列出的文件实际上不存在。 另请注意,上述内容支持子目录中的文件。


您的命令出现 rm: missing operand 错误,但我已经通过使用 ls、comm 和 xargs 解决了这个问题。无论如何,还是感谢您的帮助 pb。 - brice
在xargs后面添加-r以消除该警告(当不需要删除文件时) - pixelbeat
1
使用 grep 而不是 fgrep 来设置差异模式:`find ./ -type f -printf "%P\n" | grep -vf MANIFEST |...` - Mark Edgar

5
搞定了:
ls -1 > ALLFILES
comm -3 MANIFEST ALLFILES | xargs rm

2
是的,comm -3与我上面的解决方案等效。但要小心,因为comm期望已经排序好的输入。 - pixelbeat
干杯,最终使用了未排序的差集。 - brice
1
如果你的文件名包含空格,请使用 xargs -L 1 - djjeck

1

只是为了好玩,这是一个Perl一行代码的例子...在这种情况下并不是真正需要的,但如果你想要更加花哨和可扩展的东西,它比Bash更加自定义/可扩展。

$ ls
1   2   3   4   5   M
$ cat M
1
3
$ perl -e '{use File::Slurp; %M = map {chomp; $_ => 1} read_file("M"); $M{M}=1; \
foreach $f (glob("*")) {next if $M{$f}; unlink "$f"||die "Can not unlink: $!\n" };}' 
$ ls
1   3   M

如果您将清单传递到 STDIN 上,上述内容甚至可以更短。
perl -e '{%M = map {chomp; $_ => 1} <>; $M{M}=1; \
foreach $f (glob("*")) {next if $M{$f};unlink "$f"||die "Can not unlink: $!\n" };}' M

0

假设 MANIFEST 已经排序:

find -type f -printf %P\\n | sort | comm -3 MANIFEST - | xargs rm

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