(よくある質問がたくさんあることに注意してください(例:ここ、ここ、こことここ)しかし、彼らはディレクトリ構造が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
以前の試みで何が間違っているのかまだわからないので、この答えを受け入れません。