zsh で method_missing っぽいことをするには command_not_found_handler

きっかけ

「よく打つコマンドって typo しやすいね」「頭のスピードに手が追いついてない」「zsh 使ってて、 typo したときに勝手にコマンド名を修正してくれたらうれしいね」「zshruby の method_missing みたいなの無いのかな?」とか発言があった。

「ゆっくり打てよ」 とか思いつつ、zsh だったら出来るだろうと思って man 見たらやっぱりあった。

command_not_found_handler について

command_not_found_handler ってのを使うとそういうのが出来るようになる。man zshmisc に書いてあった。

If no external command is found but a function command_not_found_handler exists the shell executes this function with all command line arguments. The function should return status zero if it successfully handled the command, or non-zero status if it failed. In the latter case the standard handling is applied: `command not found' is printed to standard error and the shell exits with status 127. Note that the handler is executed in a subshell forked to execute an external command, hence changes to directories, shell parameters, etc. have no effect on the main shell.

以下、適当な訳。

コマンドが見つからなくて、かつ command_not_found_handler 関数が定義されている場合、zshコマンドライン全部を引数としてその関数を呼び出す。この関数は、ハンドリングに成功した場合は 0、そうでない場合は非 0 を返すべきである。後者の場合、通常のエラーハンドリングが適用される。つまり、'command not found' が標準エラー出力に出力されて、シェルの戻り値は 127 になる。なお、このハンドラはサブシェルで実行される。そのため、cd 、変数などは元のシェルには影響を与えない。

実験

さっそく試してみる。

function command_not_found_handler() {
    # $0 がコマンド名
    echo "!!! COMMAND NOT FOUND !!! $0" 1>&2

    # 引数を表示する
    shift
    local a;
    for a in $@; do
        echo "arg : $a" 1>&2
    done

    return 127
}
# notfound.zsh に書いて sorce する
% source notfound.zsh

# 適当に無いコマンドを実行する
% hogehugapiyo arg1 arg2 arg3
!!! COMMAND NOT FOUND !!! hogehugapiyo
arg : arg1
arg : arg2
arg : arg3
zsh: command not found: hogehugapiyo

ちゃんとできてる。うむっ。

これでできること

こいつを使えば typo したときに勝手に修正して実行する、とか出来るようになる。がんばればかなり賢い修正も出来ると思う。

  • git ディレクトリにいたら git 用の修正をする
  • 最近実行したコマンドを見て修正候補を決める
  • 今まで typo した結果をファイルに保存しておいて修正候補を決める

究極的には「作業用のディレクトリに移動して Enter 押しまくったら仕事終わり」とかできるはず。なんだかよくわかんないけど便利。