zshで関数内で実行したコマンドを履歴に残す

通常、コマンドを実行したら履歴に残り、Ctrl-p/n上/下の矢印キーで実行したコマンドを遡ることができる。再度同じコマンドを実行するときはとても便利。
この履歴に手動で追加するにはどうするか。

結論から言うと

printf -s 履歴に残したいコマンド

でいける。

例えばecho hogeというコマンドを履歴に残したいなら下記のようにする。

$ print -s echo hoge

Ctrl-pをするとecho hogeが出てくるはず

# historyを確認してみる
$ history | tail -n 1
 2494  echo hoge

どうしてこれが必要なのか

fzfやpecoでコマンドをまとめているときに必要になるんですよね。
dockerやgitのコマンドをfzfで選択して実行する関数を、.zshrc等に書いている人は結構多いと思います。
それ自体はめちゃくちゃ便利で、長すぎるコマンドとかオプションを覚えずにすむので重宝するのですが、 その関数で実行したものを再度実行しようとしたときにもう一度選び直さないといけないというのが面倒でした。

例えば自分はyarnのコマンドを下記のようにまとめていて、

_fzf_yarn() {
    local gitRoot=$(git rev-parse --show-cdup)
    local packageJson=$(find ${gitRoot}. -maxdepth 2  -name 'package.json')
    [ -z "$packageJson" ] && return
    local action=$(cat ${packageJson} | jq -r '.scripts | keys | .[]' \
        | fzf --preview "cat ${packageJson} | jq -r '.scripts[\"{}\"]'" --preview-window=up:1)
    [ -z "$action" ] && return
    yarn $action
}
alias yy='_fzf_yarn'

yyを実行するだけで、package.jsonに書かれたコマンドを選択して実行できる形にしています。

f:id:rasukarusan:20200728235605p:plain:w500
package.jsonを探索して中のコマンドを引っ張ってくる`yy`コマンド
ただ、この状態で「あっさっきのコマンドもう一回実行したい」となった場合にCtrl-pを押すとyyしか出てきません。
さっき実行したyarn startをもう一回実行したいだけなのに、もう一度yyと打ち、starぐらいまで入力し選択して実行、というステップを取らなければなりません。(yarn startと打つのはもっとダルいので却下)

こんなときに便利なのがprint -sという話。先程の関数の最後に追加するだけでOK。

_fzf_yarn() {
    local gitRoot=$(git rev-parse --show-cdup)
    local packageJson=$(find ${gitRoot}. -maxdepth 2  -name 'package.json')
    [ -z "$packageJson" ] && return
    local action=$(cat ${packageJson} | jq -r '.scripts | keys | .[]' \
        | fzf --preview "cat ${packageJson} | jq -r '.scripts[\"{}\"]'" --preview-window=up:1)
    [ -z "$action" ] && return
    yarn $action
+    print -s "yarn $action"
}
alias yy='_fzf_yarn'

historyに実行したyarnコマンドが残るようになりますやったね。

※ちなみにhistoryファイルに強引に書き込むという方法もあるっちゃあるが、人によってはタイムスタンプを記録するようにしていたりしてフォーマットが違うので汎用性が低くなってしまう。

終わり

仕事中煮詰まったときにコマンドのHelpを見て楽しんでいるのだが,そこで偶然見つけてとても嬉しかったので記事にした。
最初はhistoryコマンドにaddオプションみたいなものがあると思ってずっと探していたが見つからず、できないのかなと諦めていたが、まさかprintにこんなオプションあるなんて思わないよね。