删除文件中不包含文件列表的文件 - 从一个目录中删除文件,这些文件也不存在于另一个目录中

时间:2015-05-19 15:33:35

标签: bash file shell diff

我有档案文件:

./aaa
./bbb
./c/ccc
./d/ddd

我有另一个目录,其中包含相同的文件和其他目录。

./aaa
./bbb
./c/ccc
./d/ddd
./to-remove-1
./c/to-rem-ove-2

我需要删除第一个列表中没有的所有文件。

P.S。实际上,第一个列表是由命令find /some/dir/ -type f > somefile创建的。所以我们有另一个目录。但是我正在寻找一个文件。

3 个答案:

答案 0 :(得分:4)

简答

要比较的目录是AB,其中"额外"正在从B删除文件:

$ (cd A && find .) > tmp.txt
$ cd B && find . >> ../tmp.txt
$ sort ../tmp.txt | uniq -u | xargs rm
$ rm ../tmp.txt

一个班轮

$ (cd B && { (cd ../A && find .) && (find .) } | sort | uniq -u | xargs rm)

这可以避免使用命令分组来使用临时文件。请注意,如果您的文件名中包含空格,则无法使用。见"警告"下方。

解释

使用uniqfindsortxargs

uniq -u将从文件中删除所有重复的行。例如,我们可以使用findsort将目录结构缩减为以下内容:

.
.
./c
./c
./c/ccc
./c/ccc
./c/to-rem-ove-2
./d
./d
./d/ddd
./d/ddd
./to-remove-1

有了这个,uniq -u给了我们:

./c/to-rem-ove-2
./to-remove-1

您可以将其导入xargs并使用rm删除文件。例如... | uniq -u | xargs rm

逐步分解

  1. 我们有以下目录结构:

    $ tree .
    .
    ├── A
    │   ├── c
    │   │   └── ccc
    │   └── d
    │       └── ddd
    └── B
        ├── c
        │   ├── ccc
        │   └── to-rem-ove-2
        ├── d
        │   └── ddd
        └── to-remove-1
    
  2. 我们可以使用find命令列出所有目录。

    $ find .
    .
    ./B
    ./B/to-remove-1
    ./B/c
    ./B/c/ccc
    ./B/c/to-rem-ove-2
    ./B/d
    ./B/d/ddd
    ./A
    ./A/c
    ./A/c/ccc
    ./A/d
    ./A/d/ddd
    

    我们不想拥有uniq的主要目录,所以我们会这样做 cd在运行find之前进入每个目录,并将所有文件的列表保存到临时文件tmp.txt

    $ (cd A && find .) > tmp.txt
    $ (cd B && find .) >> tmp.txt
    
  3. 由于uniq -u对已排序的文件进行操作(重复的行必须 彼此相邻),我们必须使用tmp.txtsort进行排序。

    $ sort tmp.txt | uniq -u
    ./c/to-rem-ove-2
    ./to-remove-1
    
  4. 我们现在可以使用xargs删除"额外"来自B的文件。

    $ cd B
    $ sort ../tmp.txt | uniq -u | xargs rm
    

    文件现已消失:

    $ find .
    .
    ./c
    ./c/ccc
    ./d
    ./d/ddd
    
  5. 注意事项

    1. mklement0's comment,您可以使用xargs -I {} rm {} 而不是普通xargs rm来确保命令不起作用 如果文件名中有空格,那就错了。
    2. 再次使用" vanilla" xargs rm甚至不会删除目录 虽然它们列在find命令的输出中。如果你有 eB下的目录A(因此应该是rm: cannot remove ‘./e’: Is a directory 删除),您将收到类似

      的错误
      rm -r

      如果你想保留这些目录,你可以忽略它 错误。如果您需要删除它们,可以使用rm sort -r。如果您这样做,最好将它与(cd B && { (cd ../A && find .) && (find .) } | sort -r | uniq -u | xargs -I {} rm -r {}) 结合使用,以便在目录之前删除目录中的文件(如有必要)。如果您没有进行此更改,则不会更改功能,但可能会出现"错误"发布时不应该。

    3. 包含所有这些更改的整个命令是

      GetMetaData = False

答案 1 :(得分:3)

在Linux上(使用GNU实用程序)

cd "/other/dir/"
# Consider using -xtype f to also include *symlinks* to files.
find . -type f -print0 |
  grep -Fxvz -f <(cd "/some/dir" && find . -type f) |
    xargs -0 echo rm

在BSD / OSX上

cd "/other/dir/"
find . -type f |
  grep -Fxv -f <(cd "/some/dir" && find . -type f) |
    tr '\n' '\0' | xargs -0 echo rm

符合POSIX标准的变体 - 较慢

cd "/other/dir/"
find . -type f |
 grep -Fxv -f <(cd "/some/dir" && find . -type f) |
   xargs -I {} echo rm {}

以上解决方案执行干运行;删除echo以执行实际删除 此外,不使用someFile命令中的-f someFile作为grep,而是使用动态创建参考文件列表的进程替换:-f <(cd "/some/dir" && find . -type f);这项工作需要bash,而不是sh 请注意,type -f仅匹配常规文件,而符号链接与文件不匹配。 GNU Find提供-xtype -f以匹配后者。

  • find . -type f列出当前目录子树中的所有(常规)文件;
    在Linux上,-print0输出以NUL(零字节,0x0)而不是换行符终止的每个路径。
  • grep -Fxv排除(-v)字面上(-F)和完全(-x)与process substitution输出中的一行相匹配的所有输入行创建参考文件列表(-f <(...)) - 换句话说:它只输出参考列表中不存在的那些输入行。
    在Linux上,额外的-z--null-data)选项将输入分为NUL字节而不是换行符 - 假设输入是NUL分隔的。
  • xargs ... rm将通过stdin传递的输入路径转换为命令行参数,以传递给rm
    • 在Linux和BSD解决方案中,这通常只会导致一次 rm调用(因为单个命令行上的许多路径都会传递给rm) ,所有输入路径作为参数传递。
    • 在POSIX解决方案中,rm必须每个路径调用一次
  • 处理路径中的嵌入空白将帽子的提示添加到@user000001以获取其输入
    • Linux解决方案 原则上处理所有嵌入路径的空白,尤其包括换行符,其中,虽然非常罕见,但是可能。
      • 通过从开始到结束将NUL分隔的路径传递到管道中来实现此目的。
      • 那就是 在手头的情况下,不支持嵌入式换行,因为传递给Grep的-f 的引用列表必须 be 基于行按预期工作。即使选项--null-data存在,GNU Grep也要求传递给-f的文件中的搜索词是换行符 - 分离 - NUL分离不起作用。
    • BSD和POSIX解决方案从根本上只能处理嵌入的空格和标签
      • BSD解决方案使用tr '\n' '\0'用NUL替换所有换行符,当xargs -0调用xargs时,它与rm一起保留每个输入行作为自己的参数。
        • 警告:BSD Grep -z选项,但其目的非常不同。 BSD Grep的 no 等同于GNU Grep的-z--null-data)选项,使用后者是保留嵌入式换行符的唯一方法输入
      • POSIX解决方案使用xargs -I {} rm {}将每个输入行视为参数传递给rm - 缺点是必须调用rm 每行一次(路径) 使用仅限POSIX的功能,您不能同时传递带有嵌入空格或制表符的多个参数,除非您用引号括住每个标记,但这带来了自己的挑战。

答案 2 :(得分:0)

comm <(sort file1) <(sort file2) -13 | xargs -r rm