删除不包括 shell 文件中的文件列表的文件 - 从一个目录中删除另一个目录中不存在的文件



我有文件:

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

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

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

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

附言实际上第一个列表是由命令find /some/dir/ -type f > somefile制作的。所以我们还有另一个目录。但是我正在寻找处理文件。

在 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' '' | 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作为grep命令中的-f someFile,而是使用动态创建参考文件列表的进程替换:-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)与创建参考文件列表(-f <(...))的过程替换输出中的一行匹配 - 换句话说:它只输出那些在引用列表中不存在的输入行。
    在 Linux 上,附加-z(--null-data) 选项通过 NUL 字节而不是换行符将输入分解为记录 - 假设输入是 NUL 分隔的。
  • xargs ... rm将通过 stdin 传递的输入路径转换为命令行参数以传递给rm
    • 在 Linux 和 BSD 解决方案中,这通常只会导致一次rm调用(将单个命令行上容纳的路径传递给rm),所有输入路径作为参数传递给该路径。
    • 在 POSIX 解决方案中,每个路径必须调用一次rm

  • 处理路径中嵌入的空格:为他的输入@user000001帽子的尖端
    • Linux 解决方案原则上可以处理嵌入路径中的所有空格,特别是包括换行符,虽然这种情况很少见,但也是可能的。
      • 它通过从头到尾通过管道传递 NUL 分隔的路径来实现这一点。
      • 也就是说,在手头的情况下,不支持嵌入式换行符,因为传递给 Grep-f的引用列表必须基于行才能按预期工作。即使存在选项--null-data,GNU Grep 也要求传递给-f文件中的搜索词以换行符分隔 - NUL 分隔不起作用。
    • BSD和POSIX解决方案基本上只能处理嵌入式空格和制表符
      • BSD 解决方案使用tr 'n' ''将所有换行符替换为 DOL,这与 NUL 结合使用xargs -0在调用xargs时保留每个输入行作为自己的参数rm
        • 警告:BSD Grep有一个-z选项,但它的目的非常不同。BSD Grep没有等同于 GNU Grep 的-z(--null-data) 选项,使用后者是在输入中保留嵌入式换行符的唯一方法。
      • POSIX 解决方案使用xargs -I {} rm {}将每个输入行视为要传递给rm单个参数 - 缺点是必须为每行(路径)调用一次rm
        使用仅限 POSIX 的功能,您不能一次传递带有嵌入空格或制表符的多个参数,除非您用引号将每个标记括起来,但这会带来自己的挑战。

简答题

要比较的目录是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的前导目录,所以我们将 在运行find之前cd到每个目录中,并将所有文件的列表保存到临时文件中,tmp.txt.

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

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

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

    文件现在消失了:

    $ find .
    .
    ./c
    ./c/ccc
    ./d
    ./d/ddd
    

警告

  1. 根据mklement0的评论,您可以使用xargs -I {} rm {}而不是普通xargs rm,以确保命令不起作用 如果文件名中有空格,那就错了。
  2. 再次使用"香草"xargs rm不会删除目录,即使 尽管它们列在find命令的输出中。如果你有一个 目录e不在AB下(因此应该是 已删除),您将收到类似

    rm: cannot remove ‘./e’: Is a directory
    

    如果要保留这些目录,可以忽略 错误。如果需要删除它们,可以使用rm -rrm.如果要执行此操作,最好将其与sort -r结合使用,以便在必要时在目录之前删除目录中的文件。如果不进行此更改,它不会更改功能,但可能会在不应该发布"错误"时发布。

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

(cd B && { (cd ../A && find .) && (find .) } | 
sort -r | uniq -u | xargs -I {} rm -r {})
comm <(sort file1) <(sort file2) -13 | xargs -r rm

相关内容

  • 没有找到相关文章

最新更新