コマンドラインを拡張しやすくするヤツ書いた

gitなど既存のコマンドラインを拡張して新しいサブコマンドを追加する方法はいくつか考えられる。

git alias

gitの場合はgit aliasを使うことで簡単にサブコマンドを追加できる。gitのとき限定。

ラッパー

github/hubのような既存のコマンドラインをラップしたスクリプトを書き、alias hub=gitのようにaliasすることで既存の機能を保ちつつ機能を追加できる。

問題点としては、複数のラッパーによる拡張が難しくなる。例えば、ここでbubというgitのラッパーを書いたとする。githubの機能とbubの機能を拡張したい。hubは入力されたサブコマンドがhubになければgitにフォワードしている。なので、hubbubを同時に拡張するにはbubhubのラッパーとして実装することになってしまう。依存関係をハードコーディングすることになるため、まったくスケーラブルじゃない。

命名規則とext

command subcommandと入力されたらcommand-subcommandを実行するように名前解決する仕組みがよさそうだと思う。例えば、git prというコマンドはまずgit-prを探し、あれば実行し、なければgit prを実行する(そしてエラーになる)。gem uninstall allというコマンドはgem-uninstall-allgem-uninstall allgem uninstall allの順に探索されて見つかり次第実行される。

このような命名規則を基に名前解決するツールを書いた。

naoty/ext

$ go get github.com/naoty/ext
$ go get github.com/naoty/gem-uninstall-all
$ alias gem="ext gem"
$ gem uninstall all # Run gem-uninstall-all

正直、いろんな問題がありえそうだが、昨日思いついたままに書いたものなので、まだ想定できてない。gem-uninstall-railsというコマンドがあったらrailsをアンインストールできないとかありそう。

上の例で、hubbubを同時に拡張したい場合にextを使うと以下のようにできる。

$ go get github.com/naoty/hub-bub
$ alias git="ext hub"
$ git bub # Run `hub-bub`

残念ながら、hubを使いたい場合はこうするしかないような気がする。


追記(2015-07-23)

gitには、git subcommandgit-<subcommand>として名前解決して実行する機能があったことをさっき知った。なので、gitに限って言えばextのようなツールは不要だと思う。

$ cd $HOME/bin
$ vi git-hello
#!/bin/sh

echo "Hello, world!"
$ chmod +x git-hello
$ git hello
Hello, world!