LoginSignup
4
4

More than 5 years have passed since last update.

fdupesを自作してみた

Last updated at Posted at 2015-03-01

ファイルの重複チェックに便利な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}'

なんとか中間ファイルを作らない方法を試行錯誤しましたが、残念ながら力及ばず。これにて完成とします。
なかなか楽しいエクササイズになりました。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4