みんな、grep(1)ってめんどくさくない?検索なんて頻繁にやるのに4文字も打たないといけないし、.svnとかCVSとかバックアップファイルの除外が簡単にできないし、OSによるがディレクトリを指定するとディレクトリエントリを対象にしてくれたりする。おまけに正規表現なんてExtended Regexpがデフォルトでいいのに、そうするにはegrepとさらに1文字タイプしないといけない。しかも、grep patternと打ってしばらく経つのに何も出てこないと思ったら、こいつが待っているのは俺の入力!

 ほんとあほらしいよね!というわけでお届けするのがgrep(1)のラッパー、その名は「g」。特長と機能は以下です。

  • 名前が1文字。よく使うものは短い名前に。ページャはp、エディタはe、検索はg。ハフマンの法則。
  • ほぼ透過的に動作し、grep(1)の挙動を保つ。-Eがデフォルトなので正確にはegrep(1)のラッパーとも言えるが、-Fを付けても「egrep: conflicting matchers specified」などとバカみたいに怒られたりしない。
  • rsync --cvs-excludeと同じファイル除外ルールを適用する。「grep -r pattern . | fgrep -v /.svn/」のような(単純に考えて倍の時間が掛かるような)無駄は省こう。ちなみに、GNU grepのパターン指定フラグはディレクトリには適用されないので--exclude=.svnは効かない。やるなら--exclude='*.svn-base'くらいか。なお、gでなく別の名前で呼ぶと全ファイルが対象になる。私は「ln g g!」しています。
  • ディレクトリを指定すればその下を再帰的に検索。(grep -r相当)
  • ファイル名を指定せず、標準入力が端末の場合はカレントディレクトリ以下を検索。端末からの入力を検索したい珍しいケースでは「cat | g ...」などとする。
  • SUSv3 (POSIX) grep, GNU grepのオプションを理解、すべてgrep(1)に丸投げ。ただし、実装上の都合でロングオプションは「--include '*.pl'」ではなく「--include='*.pl'」と一続きで書かないと理解しない。
  • 実装はsh(1)で、内部的にfind(1)awk(1)を呼ぶ。いずれもSUSv3準拠ならほぼいけるはずだが、以下の要件がある。
    • 関数localがSUSv3にはない。外しても動くので、sh(1)が同関数をサポートしない環境では外すか、ksh(1), ash(1), bash(1)などを使う。
    • 空白等の入ったファイル名を正しく扱うため、find(1)-print0を、xargs(1)には-0を指定している。これらをサポートしない環境では、冒頭のフラグ定義の部分を調整する。

 最近はackなどを使う人もいると思うけど、シンプルなのを好むならどうぞ。Zshを使っているなら、.zshrcに「compdef _grep g g!」などと入れると良い。

追記1: GNU findだと--が理解されないので、修正しました。(0.1.0 -> 0.1.1)

追記2: 空白等が入ったファイル名を正しく扱う修正を入れました。(0.1.1 -> 0.1.2)

追記3: 空白等が入ったパターンを正しく扱う修正を入れました。(0.1.2 -> 0.2.0)


Categories : Tech