別のディレクトリから重複した項目を繰り返し削除する

別のディレクトリから重複した項目を繰り返し削除する

(よくある質問がたくさんあることに注意してください(例:ここここここここ)しかし、彼らはディレクトリ構造が1レベルであると仮定するか、答えはより複雑な複数行スクリプトであると仮定します。 )

私の状況は次のとおりです。

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

to_keep(およびサブディレクトリ)のデフォルト名を取得しto_purge(およびそのサブディレクトリ)から同じ名前のファイルを削除する単純な1行スクリプトはありますか?

私の試みはすべて失敗しました。

(どちらの場合も、コマンドが動作している状態find -printに切り替えることを目的としてコマンドをテストするために使用されました。)find -delete

初めての使用$():

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

2番目の使用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 {} +

または、要求に応じて1行で作成します。

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" {} +

このコードの唯一の問題は、ディレクトリの名前を次のように使用することです。模様別のディレクトリでファイル名を検索するために使用されます。これは、最初のディレクトリのファイルが呼び出されると、*2番目のディレクトリのすべてのファイルが削除されることを意味します。内部ファイル名保護の問題を解決できます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

以前の試みで何が間違っているのかまだわからないので、この答えを受け入れません。

関連情報