ShellScript
Bash
grep
pcre
fdupes

fdupesで検出した重複ファイルの内一番下のものを残す手順

More than 1 year has passed since last update.

fdupesは重複ファイルを手軽に削除できるコマンドだが,fdupesで削除する対象から自動的に除外できるのは重複リストの内一番上のファイルだけ.
一番下のものを残したいときは

  1. 重複リスト作成
  2. 削除リスト作成(ここで一番下のファイルを除外する)
  3. rm

の順に行う

インストールしておくコマンド

  • fdupes
  • pcregrep ... PCREパッケージをインストールすると入っている…筈

grep -Pzoでも良いかもしれないが,どうしても空行が残ってしまったのでpcregrepを使った.
そもそもBSD-grepなどはPCRE未対応なので,特にマカーさんはpcregrepbrew等で入れておくのがお勧め.

(awkerの方々から飛んでくる鉞はキャッチしたい)

重複ファイルリスト作成

[fdupes]
* -q: プログレスインジケーターを出力しない
* -r: ディレクトリ内を再起検索

単一ディレクトリ

sh
fdupes -qr ./ > "./duped.txt"

複数ディレクトリ

各ディレクトリ毎に重複を検出したいなら関数かスクリプトを用意する

fdupesout
if [ $# -ne "1" ]; then
    exit 1
fi
fdout=`fdupes -qr "./$1"`
if [ ${#fdout} -ne "0" ]; then
    echo -e "${fdout}\n" > "./duped-$1.txt"
fi

プロセス数はお好みで
ここでは削除時以外-P 4で4つフォークさせます

[xargs]
* -P: 同時に実行できる最大プロセス数
* -d: デリミタを指定

sh
ls -1 | xargs -d '\n' -P 4 -I@ fdupesout "@"

重複がなかったときに空ファイルが生成されても良いと言う場合は

sh
ls -1 | xargs -P 4 -I@ sh -c 'fdupes -qr "./@" > "./duped-@.txt"'

だけでも可

削除リスト作成

重複データ毎に改行が入るので,次の行が改行だけでない行を抽出しファイルに出力
ディレクトリ毎に重複リストが分割してあるなら先に

cat duped-* > duped.txt

でまとめる.そのためには重複リストの末尾には改行を追記しておくこと.

[pcregrep]
* -u: UTF-8モード(を使うと何故かエラー)
* -M: 複数行を対象に検索.改行\nが使用可能に

sh
pcregrep -M '[^\n]+\n(?!\n|$)' duped.txt > rmlist.txt

これで重複結果の内一番下のファイルを残すことができる

ディレクトリ毎削除リスト

ディレクトリ毎に分割した重複リストを元に削除リストもディレクトリ毎に分割するなら

[xargs]
* -I{置換文字}: プレースホルダを設定.

sh
ls -1 duped-* | xargs -P 4 -I@ sh -c 'pcregrep -M "[^\n]+\n(?\!\n|$)" @ > "rm-@.txt"'

※注意
* マルチプロセスで実行した結果を1つのファイルに書き込むと希に出力が混ざって壊れるので,上記のように一度別のリストに出力した後catでまとめるか,リスト毎に削除を行う方が良い.
* xargsでpcregrepを使う場合は「!」をエスケープすること

削除

xargs -d '\n' -I@ rm "@" < rmlist.txt

ゴミをこさえたくないなら

※ 処理規模が小さいなら以下のワンライナーでもできるかも知れない

fdupes -qr ./ | pcregrep -M '[^\n]+\n(?\!\n|$)' | xargs -d '\n' -I@ rm "@"