深酒とお昼寝で忘れる

深酒とお昼寝で忘れる

素面でも意図したことを忘れがちなしらふいとさんは、忘れる前に何かしら書き残せたらとても満足のようです

Zsh で実行に失敗したコマンドを履歴に残さない テイク2

あらすじ:zsh で(例えば、間違ってタイプして)実行に失敗したコマンドを履歴に残さないように 以前の記事 で設定しました。ところが使っているうちに一つ気に入らない振る舞いをしている点が見つかります。この度、久しぶりに設定ファイルに手を入れて改良してみることにしました。

さて何が問題だったかというと、例えば

echo $fpath | tr ' ' '\n'

というコマンドが、履歴ファイルには

echo $fpath | tr ' ' '
'

という風に保存されてしまう点です……(\nが評価されてしまっている)。これはひどい

以前の実装では、実行するコマンド(${1%%$'\n'})を一時変数(_LASTCMD)に保存し、実行直後に _LASTCMD にいくつかの処理を施し、終了ステータスが 0 ならば履歴に書き込んでいました。この『いくつかの処理』で echo ${_LASTCMD} としている箇所があったのが問題です。これは具体的には zsh のオプションで設定できる hist_reduce_blanks という機能*1を自前で実装しているところに登場します。Shell expansion に精通していたら回避できるような気もするのですが、諦めて別の手段で解決します。そもそも zsh に元来備わっている機能を自前で実装する必要があるアプローチは筋が悪い。

ということで今までは

『実行前にコマンドを一時保存 → コマンドの文字列にいくつかの処理 → コマンドを履歴ファイルに保存』

としていたところを

『実行コマンドを履歴ファイルに保存(いくつかの処理は zsh におまかせ)→ 終了ステータスを見て不要なら履歴ファイルから消去』

とします。

こちら を参考にしました。下記のような設定を .zshrc などに記入すれば良いはずです。*2

__update_history() {
  local last_status="$?"

  local HISTFILE=~/.zsh_history
  fc -W
  if [[ ${last_status} -ne 0 ]]; then
    ed -s ${HISTFILE} <<EOF >/dev/null
d
w
q
EOF
  fi
}
precmd_functions+=(__update_history)

実に 2 年越しの改良ですが(今のところ)概ね満足しています。*3

*1:コマンドの文字列から余分なスペースを省いてヒストリに保存する機能です。

*2:正直なところ fc -W/-R の挙動がいまいち良く分かってないのですが、正しく動いているように見えます。

*3:実際には諸事情でもう少し違った実装にしていますが、基本方針はこの通りです。