ほっしーの技術ネタ備忘録

技術ネタの備忘録です。基本的に私が忘れないためのものです。他の人の役にも立つといいなぁ。

ディスク内総検索その2(高速化してみた)

FreeBSD システムのディスク全体から特定の記述を探したい場合、ありますよね。 例えば IPv6 プレフィックスが変わったので全体の設定を変えなきゃいけない時とか。どこに設定したっけ…?みたいな。

そんな時はざっくり全体から検索してしまいましょう。

$ sudo find / -path '*/dev/*' -prune -o -path '*/proc/*' -prune -o -path '*/var/log/*' -prune -o -name '*.log' -prune -o -path '*/basejail/*' -prune -o -type f -print0 | sudo xargs -0 grep "search target" | tee result.txt

…いやなっげぇよ!!

まぁ、以前同じ内容の記事を書いたときのやり方そのままですね。 hoshizuki.hateblo.jp

やってることは、 find/ 起点で実行して、別に探さなくてもいいパスを -path '*/var/log/*' -prune -o でどんどん落としています。 -o つまり OR の短絡評価によって途中でマッチしたパスを間引いていって、最後に残ったものを -print0 します。 前回の記事と違って、最近は -path を使ってワイルドカードでの一致にしていますね。正規表現面倒で嫌いなので。

あとはおなじみ xargs からの grep です。まぁまぁ単純でシンプルなコマンドです。 出力結果は stdout でリアルタイムに眺めつつ、一応ファイルにもロギングしているおまけ付き。

なお、前回の記事に書いた通り、ディレクトリ名にスペースが含まれていると xargs につっこんだときに分解されて悲しい目に遭うので、 -print0 で出して xargs -0 で受けます。

シンプルだけれども、できればマウントポイントごとフィルタ除外したいっていうか、 grep の前の find は一応全ディレクトリを見に行くので、その無駄すらも何とかしたい。

そこでこんな改良。

$ ls / | grep -v -e home -e sys -e mnt | sudo xargs -n 1 -I {} find {} -type f | sudo xargs grep "search target" | tee ~/grep_result.txt

/ 直下のディレクトリ一覧を ls で取得して、 grep でざっくりフィルタリング。 /home とか /sys とか /mnt とか探しても仕方ないマウントポイントはいくつもあります。 ここでは -vgrep の論理を反転します。 -e で検索対象を複数書くのと組み合わせると、「いずれにもマッチしないもの」のみを抽出できます。

んでそれを xargs で順に find に投げて、全ファイルの一覧から grep します。

しかし、特に ezjail でありがちな、いろんな所でいろんな所を nullfs でマウントしてると、これだけだとまだ弱い。マウントポイントはイマドキ / だけじゃない。

そんな訳でこんなパターンが最近のお気に入り。

$ mount -p | awk '{print $2}' | grep -v -e '/basejail$' -e '/dev$' -e '/dev/fd$' -e '/proc$' -e '/home$' -e '/var/ports$' -e '^/mnt/external$' | sudo xargs -n 1 -I {} find {} -mount -path '/usr/src/*' -prune -o -path '/usr/ports/*' -prune -o -path '*/basejail/*' -prune -o -path '*/var/log/*' -prune -o -type f -print0 | sudo xargs -0 grep 'search_target' | tee result.log

さらに長くなったナァ……

mount -p で現在のマウント状況を fstab フォーマットでダンプしてくれます。 awk で第2フィールド、つまりマウントポイントのみのリストにして、これをさっきの例のように grep -v -e ... -e ... でフィルタします。最初の grep ではマウントポイントしか出てこないので、例えば末尾のディレクトリ名などでパターンパッチングします。

次は同じように xargs からの find ですが、最初に -mount を付けるのがミソ。これでマウントポイントになってるディレクトリで検索を打ち切ります。そのマウントポイントが検索対象ならば別途 xargs に入ってくるレコードによって補完されますし、対象外ならそのまま無視になります。

後は同じように -path '...' -prune -o でマウントポイント以外のフィルタリングをしてから、 xargsgrep に流して終わり。

これでだいぶ高速になりました。コマンドは長いけどなー……