UNIXライクなOS上で作業をするとき、grepコマンドはなくてはならない存在です。そんな基本的かつ古典的なユーティリティであるgrepですが、使いにくい面もあります。
2013年のはじめころから、grepに取って代わるコマンドとして「The Silver Searcher」 (またの名を「ag」 )が注目されはじめました。
今回のレシピでは、「 The Silver Searcher」(以下ag)をUbuntuで使用する方法を紹介します。
The Silver Searcher(ag)とは?
プログラムを書いていると、ソースコード全域にわたって文字列を検索したい、ということはよくありますよね。そのようなときにもgrepコマンドが活躍するわけですが、プロジェクトのディレクトリには検索したくないファイルというものも存在します。
たとえばバージョン管理システムが使っている「.git」や「.svn」といったディレクトリは、検索対象に含めたくありません。またバージョン管理下にないファイル(makeした後に生成されるファイルなど)も無視したいところです。これをgrepで実現しようとすると、パイプやサブシェルを組み合わせて、少々面倒なワンライナーを書く必要があります。
こういった問題を解決するため、ack というプログラムが作られました[1] 。ackは以下のような特徴を持つ、開発者向けの賢いgrepです。
デフォルトでディレクトリツリーを再帰検索
.gitなどを暗黙的に除外
検索するファイルタイプをオプションで指定可能(--perl、など)
拡張子だけでなく、shebangを見てファイルタイプを判別可能
検索結果をファイル単位でわかりやすくまとめて表示
このようにackは非常に高性能なのですが、Perlで実装されていることもあり、動作速度に難があります。agは開発者が「A code searching tool similar to ack, with a focus on speed.」と紹介しているとおり、「 高速なack」を目指して開発されているプログラムです。「 ackに比べて3から5倍高速」を謳っており[2] 、そして実際非常に高速です。
ためしにUbuntu 13.04のカーネルのソースに対して文字列を検索し、速度を比較してみました[3] 。
これは筆者が原稿を書いている環境[4] で、OSを起動した直後(キャッシュにデータが乗っていない状態)にそれぞれのコマンドを実行して計測しました。
カーネルのソースパッケージを取得して展開
$ apt-get source linux-image-3.8.0-27-generic
kthreadという文字列でソース全体を検索する
$ time grep -r kthread * > /dev/null
real 0m11.043s
user 0m0.908s
sys 0m1.472s
$ time ack-grep kthread > /dev/null
real 0m16.991s
user 0m7.484s
sys 0m1.324s
$ time ag kthread >/dev/null
real 0m4.285s
user 0m1.180s
sys 0m1.748s
ソース全体の検索にかかった時間
grep ack ag
11.043 16.991 4.285
見てのとおり、実行時間において4倍、grepと比べても3倍近い差が出ました。
agは内部でpthreadを用い、ファイルを並列に検索しています。小さなプロジェクトではそれほど差は出ませんが、カーネルくらいファイルやディレクトリの数が増えてくると、ackとagの差は顕著になってきます。
図1 ack/ag実行時のシステム負荷。agはackに比べ、マルチCPUをうまく利用していることがわかる
パッケージのインストール
Debian unstableやUbuntu 13.10では、silversearcher-agというパッケージが用意されています。以下のようにパッケージをインストールすることで、agコマンドが使用できるようになります。またbash用のcompletionも同梱されていますので、シェルにbashを使っている場合、Tabキーでオプションを補完することも可能です。
Ubuntu 13.04向けには、PPAにパッケージを用意しました。筆者のPPAを追加して「apt-get update」を実行した後に、インストールを行ってください。今後Debian unstableのパッケージが更新された場合、このPPAに都度バックポートを行う予定です[5] 。前述のとおり、Ubuntu 13.10以降はPPAを追加しなくてもUniverseのパッケージを利用することができますが、筆者のPPAを追加することで最新版を利用できるかもしれません。
agをパッケージからインストールする
$ sudo apt-get install silversearcher-ag
Ubuntu 13.04では、筆者のPPAを追加する
$ sudo apt-add-repository ppa:mizuno-as/silversearcher-ag
[sudo] password for mizuno:
以下のPPAをシステムに追加しようとしています:
Backport PPA for The Silver Searcher from Debian sid / Ubuntu development branch.
The Silver Searcher (a.k.a. ag)のバックポート用PPAです。
詳しい情報: https://launchpad.net/~mizuno-as/+archive/silversearcher-ag
[ENTER] を押すと続行します。ctrl-c で追加をキャンセルできます
$ sudo apt-get update
図2 オプションの補完。シェルにbashを利用しているならば、Tabキーでの補完が有効になる
Debianパッケージング道場
実は、Debianでsilversearcher-agパッケージをメンテナンスしているのは筆者です。なぜagのパッケージングとメンテナンスをすることになったのか、その経緯を少し紹介しておきます。
2013年2月、京都大学で「Debianパッケージング道場」 というイベントが行われました。お題として「参加者は何かパッケージにしたいものを持ってきて、それをパッケージ化する」というものがあり、筆者はたまたま、そのころ話題になりつつあったagを選択しました。そのときに作成したパッケージがunstableに入り、そしてUbuntuでも、13.10から使えるようになったというわけです。
東京エリアDebian勉強会 や関西Debian勉強会 では、未来のDebian開発者を育てることを目標に、毎月勉強会を開催しているほか、パッケージング道場のようなイベントを不定期に開催しています。
DebianやUbuntuの開発に興味のある方は、参加してみてはいかがでしょうか。
agで検索する
それでは実際にagを使って検索を行ってみましょう。コマンド名であるagに続いて検索したいパターンを記述するだけで、カレントディレクトリ以下から再帰的にパターンを検索します。
カレントディレクトリ以下からhogeという文字列を検索する
$ ag hoge
検索対象を指定したい場合は、パターンの後に対象のディレクトリやファイルを指定します。ファイルを指定した場合はそのファイルから、ディレクトリを指定した場合は指定されたディレクトリ以下を再帰的に検索します。
/var/log以下からinstallという文字列を検索する
$ ag install /var/log
grepに対するzgrepのように、agはgzipで圧縮されたファイルの中身を展開せずに検索することができます。そのためには「-z」オプションを使用します。たとえば、gzipで圧縮されているパッケージのchangelogを検索したいような場合は、以下のようにします。
gzipで圧縮されたファイルから文字列検索する
$ ag -z 'new upstream release' /usr/share/doc
マッチした行ではなく、パターンが含まれるファイル名だけを表示させることもできます。また-Lオプションを使うと動作が逆転し、パターンが含まれないファイル名を表示します。
カーネルのソースコードから、kthreadという文字列が含まれるファイルのリストを表示する
$ ag -l kthread
init/main.c
init/Kconfig
mm/kmemleak.c
mm/vmscan.c
(……略……)
他にも様々なオプションが用意されています。詳細はmanを読んで確認してください。
検索対象について
agは標準でカレントディレクトリ以下の全ファイルを検索対象としますが、暗黙的に除外されるファイルがいくつか存在します。
まず、「 .(ドット) 」ではじまる隠しファイルは検索の対象になりません。隠しファイルを検索対象に含めたい場合は、「 --hidden」オプションを併用する必要があります。たとえば以下の例ではホームディレクトリ以下で「emacs」という文字列を検索しています。そのままではマッチするファイルは1つもありませんが、「 --hidden」オプションをつけることで、「 .emacs.d」ディレクトリ以下が検索されています。
--hiddenオプションの有無の違い
$ ag emacs
← 何も表示されない=マッチするファイルが存在しない
$ ag --hidden emacs
.emacs.d/init.el:6:;; Keywords: .emacs init
.emacs.d/init.el:10:(setq load-path(cons "~/.emacs.d/elisp" load-path))
.emacs.d/init.el:11:(let ((default-directory "~/.emacs.d/elisp"))
(……略……)
agはバージョン管理システムとの相性も抜群です。「 .git」のようなディレクトリを検索対象に含めないのは前述のとおりですが、それに加えて「.gitignore」や「.hgignore」に記載されているパターンも読み取り、検索対象から自動的に除外します。
たとえばエディタが自動的に作成したバックアップファイルをgitに認識させないため、「 .gitignore」に「*~」などと書いている人は多いと思います。この状態では、agもまた「*~」ファイルを無視します。
gitの除外設定が反映される
$ cat .gitignore
*~
$ echo hoge > hoge~
$ cat hoge~
hoge
$ ag hoge
← 何も表示されない=hoge~は検索されない
「--ignore」オプションには、除外するパターンをその場で記載することができます。以下の例では「hoge.txt」に「hoge」という文字列が存在しますが、拡張子「txt」のファイルを無視するようパラメータを指定すると、検索対象から除外されているのがわかります。
毎回「--ignore」を指定するのが面倒な場合は、「 ~/.agignore」ファイルにパターンを記載することで、同様の動きを実現できます。
特定のパターンを除外する
$ ag hoge
hoge.txt ← hoge.txtがマッチしている
1:hoge
$ ag --ignore *.txt hoge
← 何も表示されない=拡張子がtxtのファイルは検索されない
「-u」オプションを指定すると、agは隠しファイルも含めたすべてのファイルを検索の対象とします。「 .gitignore」や「.agignore」は無視され、同時に「--ignore」オプションが指定された場合、これも無視します。とにかくすべてのファイルから検索したい場合に利用してください。
Emacsからagを使う
プログラムを書いていて、キーワードをgrepしたいシチュエーションを考えてみましょう。プログラムはなんらかのテキストエディタに類するもので書くのが一般的ですから、わざわざ端末を開いてコマンドを叩きたくはありません。エディタや統合開発環境上から検索できるのが望ましいでしょう。そこで、agのEmacs用のフロントエンド「ag.el」を紹介します。
Debian unstableやUbuntu 13.10には、silversearcher-ag-elというパッケージがあります。まずこのパッケージをインストールしましょう。もちろんUbuntu 13.04向けには、前述のPPAに用意してあります。
silversearcher-ag-elをインストールする
$ sudo apt-get install silversearcher-ag-el
パッケージをインストールしたら、Emacsを起動してください。これだけでagをEmacs上から叩くことが可能になります。require処理はパッケージ側で行っているため、個人の.emacsには何も書く必要はありません。
Emacs上で「M-x ag」を実行してください。するとミニバッファに「Search string:」と表示されますので、検索したいパターンを入力します。続いて検索対象とするディレクトリを聞いてきますので、ディレクトリへのパスを入力してください。この際、デフォルトで「現在開いているバッファのファイルが存在するディレクトリ」が指定されています。
すると検索結果が「*ag*」バッファに表示されます。このバッファ上で、編集したい行にカーソルを動かしてEnterを押せば、該当するファイルを新しいバッファで開き、該当箇所へカーソルがジャンプします。
ag.elには「ag-project」という関数も用意されています。これは現在開いているファイルがバージョン管理下にある場合、そのコードリポジトリのルートディレクトリを、自動的に検索対象に指定するという動作を行います。つまり検索対象ディレクトリの指定を省略して、コードツリー全体を検索することができるわけです。git cloneしたソースコードをいじっているような場合は、agよりもag-projectを利用すると便利でしょう。
マッチした部分をハイライトする
agのバージョン0.14以降には、マッチした部分の色を指定する「--color-match」というオプションが実装されています。.emacsに以下の設定を追加しておくと、*ag*バッファのマッチした部分をハイライト表示することができます。
ag-highlight-searchを有効にする
(setq ag-highlight-search t)
図3 ag-highlight-searchの有無による違い。ag-highlight-searchを有効にしておくと、マッチした部分がハイライト表示され、見やすくなる
おわりに
詳細な機能面では、agはまだまだackには及ばない部分があるのも事実です。しかし基本的な機能はすでに実装されていますし、速度に関して言えば圧倒的なアドバンテージがあります。まだgrepで頑張っている人はもちろん、ackをすでに使っている人であっても、agの利用を検討する価値は充分にあると言えるでしょう。