ファイルの重複チェックに便利なfdupesコマンドですが、入ってない環境でも使えるようにワンライナーを書いてみました。車輪の再発明です。
find . -type f | xargs md5sum > /tmp/a; grep -f <(awk '{print $1}' /tmp/a | sort | uniq -c | awk '{if($1 > 1) print $2}') /tmp/a | sort | perl -lane 'push @{$a{$F[0]}}, $F[1]; END {print join "\n", @{$a{$_}}, "" for keys %a}'
fdupesとは
同一ファイルを探してくれるコマンドです。
重複したファイルがゴミじゃないか?とか、共通化出来ないか?とか、シンボリックリンクに置き換えられないか?などの調査の取っ掛かりに使えます。
# fdupes -r .
./images/milestone_done.png
./images/task_done.png
./images/arrow_collapsed.png
./images/bullet_arrow_right.png
./help/bg/wiki_syntax_detailed.html
./help/pl/wiki_syntax_detailed.html
./help/sv/wiki_syntax_detailed.html
./help/bs/wiki_syntax_detailed.html
...
空行ごとにグルーピングされたもの同士でdiff取ってみれば、それぞれすべて同じということが分かります。
# diff ./images/milestone_done.png ./images/task_done.png
# diff ./images/arrow_collapsed.png ./images/bullet_arrow_right.png
# diff ./help/bg/wiki_syntax_detailed.html ./help/pl/wiki_syntax_detailed.html
# diff ./help/pl/wiki_syntax_detailed.html ./help/sv/wiki_syntax_detailed.html
md5sum
これを自作するには、findでカレント配下の一覧を取りつつ、md5sumで各ファイルのフィンガープリントを計算します。やりたいことは、これが同じものをグルーピングさせて表示させたい、というだけです。
# find . -type f | xargs md5sum
12203dd0b04ea085217b93b0871053b5 ./dispatch.fcgi.example
61affd115c8d129c258713445d558301 ./javascripts/datepicker.js
50e838e3ddd98dc7845db32539fe980f ./javascripts/attachments.js
25e711a474b72694444e46441a45daef ./javascripts/i18n/datepicker-hr.js
4ec013ff08274062b9affe005d6e0bad ./javascripts/i18n/datepicker-it.js
a67ea4ea3a1305230d78c663d781aec5 ./javascripts/i18n/datepicker-cs.js
0a30262822d33944bef4f32f2c40dabd ./javascripts/i18n/datepicker-nl.js
d379d734a97928692297893bf538c503 ./javascripts/i18n/datepicker-eu.js
01d3088ea6837a13205cea656d042b2c ./javascripts/i18n/datepicker-fa.js
5fca3c9b96c5ef5142521f7e650edf5f ./javascripts/i18n/datepicker-th.js
...
左側のフィンガープリントだけを抽出し、uniq -c
で数を数えます。(わかりやすくするため、数字順で逆ソートもしてます)
# find . -type f | xargs md5sum | awk '{print $1}' | sort | uniq -c | sort -nr
43 8ec587ffbce2b400b7a3abc1087c61f2
41 150d3ca182103800e898533daac8a93c
6 c49e65f5c02ababbaa0c9a9968424950
2 980dcfdb816ce626e4d2df5c2a308549
2 40c58172e0c52eee4deb5227ec37f0cf
1 ff96c02d8f18116bb3f005f2c8b86e91
1 feb0aa630b9a43ca8b40ca6c85038327
1 fd6a3f960f0d6e1ce7e6466d592453fe
1 fba036d7348ff28f7ab7dacbc52bc32a
1 fb00cc94dfc8f319dda707b957aae8a6
...
カウント数が2以上のものを抽出し、フィンガープリントだけ表示させます。
# find . -type f | xargs md5sum | awk '{print $1}' | sort | uniq -c | awk '{if($1>1) print $2}'
150d3ca182103800e898533daac8a93c
40c58172e0c52eee4deb5227ec37f0cf
8ec587ffbce2b400b7a3abc1087c61f2
980dcfdb816ce626e4d2df5c2a308549
c49e65f5c02ababbaa0c9a9968424950
これを一旦ファイルに落とし、grep -f
の材料とします。
# find . -type f | xargs md5sum | awk '{print $1}' | sort | uniq -c | awk '{if($1>1) print $2}' > /tmp/g
元の一覧からgrep -f
します。
# find . -type f | xargs md5sum | grep -f /tmp/g
c49e65f5c02ababbaa0c9a9968424950 ./javascripts/jstoolbar/lang/jstoolbar-en.js
c49e65f5c02ababbaa0c9a9968424950 ./javascripts/jstoolbar/lang/jstoolbar-sq.js
c49e65f5c02ababbaa0c9a9968424950 ./javascripts/jstoolbar/lang/jstoolbar-he.js
c49e65f5c02ababbaa0c9a9968424950 ./javascripts/jstoolbar/lang/jstoolbar-uk.js
c49e65f5c02ababbaa0c9a9968424950 ./javascripts/jstoolbar/lang/jstoolbar-en-gb.js
c49e65f5c02ababbaa0c9a9968424950 ./javascripts/jstoolbar/lang/jstoolbar-az.js
150d3ca182103800e898533daac8a93c ./help/bg/wiki_syntax.html
8ec587ffbce2b400b7a3abc1087c61f2 ./help/bg/wiki_syntax_detailed.html
150d3ca182103800e898533daac8a93c ./help/sl/wiki_syntax.html
8ec587ffbce2b400b7a3abc1087c61f2 ./help/sl/wiki_syntax_detailed.html
...
ソートして同一フィンガープリント同士でグルーピングします。
# find . -type f | xargs md5sum | grep -f /tmp/g | sort | perl -MData::Dumper -lane 'push @{$a{$F[0]}}, $F[1]; END {print Dumper %a}'
$VAR1 = '150d3ca182103800e898533daac8a93c';
$VAR2 = [
'./help/ar/wiki_syntax.html',
'./help/az/wiki_syntax.html',
'./help/bg/wiki_syntax.html',
'./help/bs/wiki_syntax.html',
'./help/ca/wiki_syntax.html',
'./help/da/wiki_syntax.html',
'./help/el/wiki_syntax.html',
'./help/en-gb/wiki_syntax.html',
(中略)
'./help/zh/wiki_syntax.html'
];
$VAR3 = 'c49e65f5c02ababbaa0c9a9968424950';
$VAR4 = [
'./javascripts/jstoolbar/lang/jstoolbar-az.js',
'./javascripts/jstoolbar/lang/jstoolbar-en-gb.js',
'./javascripts/jstoolbar/lang/jstoolbar-en.js',
'./javascripts/jstoolbar/lang/jstoolbar-he.js',
'./javascripts/jstoolbar/lang/jstoolbar-sq.js',
'./javascripts/jstoolbar/lang/jstoolbar-uk.js'
];
$VAR5 = '8ec587ffbce2b400b7a3abc1087c61f2';
$VAR6 = [
'./help/ar/wiki_syntax_detailed.html',
'./help/az/wiki_syntax_detailed.html',
...
グループごとに空行で区切るように表示を工夫すればほぼ完成です。
# find . -type f | xargs md5sum | grep -f /tmp/g | sort | perl -lane 'push @{$a{$F[0]}}, $F[1]; END {print join "\n", @{$a{$_}}, "" for keys %a}'
./help/ar/wiki_syntax.html
./help/az/wiki_syntax.html
./help/bg/wiki_syntax.html
./help/bs/wiki_syntax.html
./help/ca/wiki_syntax.html
./help/da/wiki_syntax.html
./help/el/wiki_syntax.html
./help/en-gb/wiki_syntax.html
(中略)
./help/vi/wiki_syntax.html
./help/zh/wiki_syntax.html
./javascripts/jstoolbar/lang/jstoolbar-az.js
./javascripts/jstoolbar/lang/jstoolbar-en-gb.js
./javascripts/jstoolbar/lang/jstoolbar-en.js
./javascripts/jstoolbar/lang/jstoolbar-he.js
./javascripts/jstoolbar/lang/jstoolbar-sq.js
./javascripts/jstoolbar/lang/jstoolbar-uk.js
./help/ar/wiki_syntax_detailed.html
./help/az/wiki_syntax_detailed.html
...
リファクタリング
bashの<()
を使ってgrep -f
用の中間ファイルを消します。
# find . -type f | xargs md5sum | grep -f <(find . -type f | xargs md5sum | awk '{print $1}' | sort | uniq -c | awk '{if($1 > 1) print $2}') | sort | perl -lane 'push @{$a{$F[0]}}, $F[1]; END {print join "\n", @{$a{$_}}, "" for keys %a}'
find . -type f | xargs md5sum
が重複しているので中間ファイルとして切り出します。(やむなく)
# find . -type f | xargs md5sum > /tmp/a; grep -f <(awk '{print $1}' /tmp/a | sort | uniq -c | awk '{if($1 > 1) print $2}') /tmp/a | sort | perl -lane 'push @{$a{$F[0]}}, $F[1]; END {print join "\n", @{$a{$_}}, "" for keys %a}'
なんとか中間ファイルを作らない方法を試行錯誤しましたが、残念ながら力及ばず。これにて完成とします。
なかなか楽しいエクササイズになりました。