递归地从另一个目录中删除重复项

递归地从另一个目录中删除重复项

(注意有很多类似的问题(例如这里,这里,这里, 和这里)但他们要么假设目录结构是一层的,要么答案是更复杂的多行脚本。)

这是我的情况:

.
├── to_keep
│   ├── a
│   │   └── duplicate1.txt
│   └── b
│       ├── duplicate2.txt
│       └── unique1.txt
└── to_purge
    ├── c
    │   └── duplicate1.txt
    └── d
        ├── duplicate2.txt
        └── unique2.txt

是否有一个简单的一行脚本将使用to_keep(及其子目录)中找到的基本名称并从to_purge(及其子目录)中删除具有相同名称的文件?

我的两次尝试都失败了。

(在这两种情况下,我都用来find -print测试该命令,目的是将其切换到find -delete它工作时的状态。)

第一个用途$()

find ./to_purge/ -print -name $(find ./to_keep/ -type f -printf "%f\n")
find: paths must precede expression: `duplicate2.txt'

第二个用途xargs

find ./to_keep/ -type f -printf "%f\n" | xargs --max-args=1 find ./to_purge/ -print -name
./to_purge/
./to_purge/c
./to_purge/c/duplicate1.txt
./to_purge/d
./to_purge/d/duplicate2.txt
./to_purge/d/unique2.txt
./to_purge/
./to_purge/c
./to_purge/c/duplicate1.txt
./to_purge/d
./to_purge/d/duplicate2.txt
./to_purge/d/unique2.txt
./to_purge/
./to_purge/c
./to_purge/c/duplicate1.txt
./to_purge/d
./to_purge/d/duplicate2.txt
./to_purge/d/unique2.txt

这两种尝试都不起作用。我有什么错吗?

答案1

下面将查找其中或之下的所有常规文件,并将批量调用这些文件的内./to_keep联脚本。sh -c对于每批路径名,内联脚本将调用一次以查找其下具有相同名称的find常规文件。将打印./to_purge下面这些文件的路径名(要删除它们,请在后面添加)。./to_purge-delete-print

find to_keep -type f -exec sh -c '
    for pathname do
        set -- "$@" -o -name "${pathname##*/}"
        shift
    done; shift
    find to_purge \( "$@" \) -type f -print' sh {} +

或者,根据要求,在一行上:

find to_keep -type f -exec sh -c 'for pathname do set -- "$@" -o -name "${pathname##*/}"; shift; done; shift; find to_purge \( "$@" \) -type f -print' sh {} +

内联脚本为其最后一行使用的命令构造一个 OR-name测试列表。该循环根据外部传递给它的find每个路径名的文件名部分在位置参数中构造此列表。find

这涉及所有允许的文件名,包括包含空格、制表符和换行符的文件名。再次,为了删除文件,在代码后面添加-delete(或-exec rm {} +) 。-print

作为一个简短的脚本,它将“保留目录”和“清除目录”作为命令行参数:

#!/bin/sh

keepdir=$1
purgedir=$2

find "$keepdir" -type f -exec sh -c '
    dir=$1; shift
    for pathname do
        set -- "$@" -o -name "${pathname##*/}"
        shift
    done; shift
    find "$dir" \( "$@" \) -type f -print' sh "$purgedir" {} +

此代码的唯一问题是它将使用一个目录中的名称作为图案用于查找其他目录中的文件名。这意味着如果调用第一个目录中的文件*,则第二个目录中的所有文件都将被删除。您可以修复保护内部文件名的问题find

for pathname do
    sane=$( printf "%s\n" "${pathname##*/}" | sed "s/[[*?]/\\&/g" )
    set -- "$@" -o -name "$sane"
    shift
done; shift

对内联sh -c脚本中的循环的修改会转义[,*?字符(否则用作文件名通配模式)。该脚本现在不会处理以下文件名结尾在换行符中(由于使用了命令替换),但这可能是人们可以忍受的事情。

答案2

典型的,一发布我就找到答案!

find ./to_keep/ -type f -exec basename '{}' \; | xargs --max-args=1 find ./to_purge/ -name | xargs --max-args=1 rm

我不会接受这个答案,因为我还不知道我之前的尝试出了什么问题。

相关内容