inode番号が重複するファイルを表示(解説編)
inode番号が重複するファイルを表示 - 日々之迷歩、初心者置いてけぼりでしたので解説をば。
動作環境は、Mac OSX Yosemite。HomebrewでGNU grep(ggrep)を追加でインストール。インストールは下記を参考に。
高速化したGNU grepをインストールする - Qiita
下記のように、基本操作の組み合わせで考えてみると比較的簡単。
- /usr/bin以下の重複するinode番号をリストアップ
ls -i /usr/bin
の結果から、リストアップされたinode番号が含まれる行を抽出
重複に関連する操作の基本はsortからのuniq、基本じゃ。探したいキーワードを含む行の抽出はgrep、基本じゃ。この基本の組み合わせを一時ファイル無しでワンライナーする場合、<(コマンド)記法が必要になってくる(ksh、bash、zsh限定)。
それでは実際に解いていく。まずは重複するinode番号をリストアップしてみる。ls -i /usr/bin
で、inode番号とコマンドファイル名を出力。(行が多いのでheadで最初の10行だけ表示)
$ \ls -i /usr/bin | head
8569390 2to3
8569390 2to3-
8569393 2to3-2.7
8569394 2to32.6
15236776 BuildStrings
15236795 CpMac
15236803 DeRez
15236821 GetFileInfo
15236887 MergePef
15236890 MvMac
ここからsort -k1,1
でinode番号をキーにして整列。-k1,1
は1カラム目で並べ替えるという指定。
$ \ls -i /usr/bin | sort -k1,1 | head
1723714 escputil
1724489 cups-calibrate
1744470 oauth
1744531 prettify_json.rb
1744587 edit_json.rb
1744661 update_rubygems
8203291 ipmitool
8204825 ditto
8209715 scp
8209716 sftp
整列した結果について、awk '{print $1}'
で、1カラム目のinode番号のみ出力。
$ \ls -i /usr/bin | sort -k1,1 | awk '{print $1}' | head
1723714
1724489
1744470
1744531
1744587
1744661
8203291
8204825
8209715
8209716
ここから重複する行のみを抽出すればよい。まずはuniq -c
で実際に重複している数を確認してみる。1カラム目が重複している数になる。
見やすくするためsort -rn -k1,1
で重複が多い順に並べ替えて確認。38個も重複してるのがあるな・・
$ \ls -i /usr/bin | sort -k1,1 | awk '{print $1}' | uniq -c | sort -rn -k1,1 | head
38 8557661
35 8546497
15 8578607
6 8551790
5 8569390
4 15236764
3 8541772
3 8486486
3 18734005
3 18730932
さて、重複しているものがあることがわかった。ちょっと戻って、uniq -d
で重複している行のみを抽出。-dオプションは重複する行のみを表示。これで後半の検索で利用するキーワードが準備完了。
$ \ls -i /usr/bin | sort -k1,1 | awk '{print $1}' | uniq -d | head
8214826
8486486
8541772
8546497
8551346
8551790
8555174
8555191
8555195
8557011
この結果を一時ファイルに保存。更にls -i /usr/bin
も一時ファイルに保存。そしてgrep -fオプションで、検索キーワードが書かれたファイルを指定し、検索するファイルを指定。結果を見やすくsortする。lessとmore、grep兄弟等がinode番号が同じことが分かる。
$ \ls -i /usr/bin | sort -k1,1 | awk '{print $1}' | uniq -d > temp1
$ \ls -i /usr/bin > temp2
$ grep -f temp1 temp2 | sort -k1,1 | head
8214826 less
8214826 more
8486486 egrep
8486486 fgrep
8486486 grep
8541772 groups
8541772 id
8541772 whoami
8546497 binhex.pl
8546497 crc32
$ rm temp1 temp2
さて、これで目的は果たしたのだが、一時ファイルを作らなければいけないのがちょっとイケてない。そこで<(コマンド)記法が役に立つ。ksh、bash、zsh限定だが、コマンドの実行結果をファイルとして指定出来る。
ちょっとした例を。lsの出力とls -aの出力の差分を取ってみる。こんな感じで便利な機能だと思う。
$ diff <(ls) <(ls -a) | head
0a1,48
> ./
> ../
> .CFUserTextEncoding
> .DS_Store
> .FontForge/
> .Rapp.history
> .Trash/
> .Xauthority
> .atom/
さて、<(コマンド)記法をふまえて、改めてワンライナーを考えてみる。ggrep -f -
と指定すると、検索キーワードを標準入力から受け取ることが出来る。(grepじゃなくてggrepなのは、Mac標準のgrepはBSD系で、grep -f -の指定が出来なかったため)
さあ、これで最終回答じゃ!
$ \ls -i /usr/bin | sort -k1,1 | awk '{print $1}' | uniq -d | ggrep -f - <(\ls -i /usr/bin) | sort -k1,1 | head
8214826 less
8214826 more
8486486 egrep
8486486 fgrep
8486486 grep
8541772 groups
8541772 id
8541772 whoami
8546497 binhex.pl
8546497 crc3
さて、いかがだっただろうか?シェル芸の基本がギュッと詰まっているのではないかと思う。さあ、これであなたもシェル芸人!
<<<追記>>>
MacのBSDなgrepでも、一時ファイルを作らずワンライナ書けた。難しく考えすぎてたかも・・・<(コマンド)の記法がたくさん出てくると見にくい気もするが。
$ grep -f <(\ls -i /usr/bin | sort -k1,1 | awk '{print $1}' | uniq -d) <(\ls -i /usr/bin) | sort -k1,1 | head
8214826 less
8214826 more
8486486 egrep
8486486 fgrep
8486486 grep
8541772 groups
8541772 id
8541772 whoami
8546497 binhex.pl
8546497 crc32