ロゴテキスト ロゴ

    zshのhistoryをインクリメントサーチで削除する

    zshのhistoryをインクリメントサーチで削除する

    コマンドを実行する時「あ、、間違ったコマンド打っちゃった・・😇」となる時はありませんか?

    私は日常茶飯事でやってる気がします。笑




    するとコマンドの履歴にはドンドン溜まらなくていいコマンドが溜まってしまいます。。

    # コマンドの履歴
    $ history -n 1
    hd
    op .
    cat ~/.zsh_history
    miss #誤って入力してしまったコマンド
    git log
    git log$  #誤って入力してしまったコマンド
    history



    今までの履歴から検索して再度コマンドを実行する、という事は良くあると思ってて

    ↑ のように missgit log$gti branch のような

    誤って打ってしまったコマンドが検索履歴に残り続けてしまうのもめんどくさいですよね。



    そこで打ち間違えたコマンドもインクリメントサーチを使って消してしまおう!

    という事をやってみます。

    検証した環境

    1 fzf 0.42.0(brew)

    インクリメントサーチでhistoryを消す

    hisotryについて

    historyコマンドとはbashzshなどで使用出来るコマンド。

    ユーザーが以前に入力したコマンドのリストを表示します。

    $ history
    op .
    miss
    git log
    git log$
    history


    この一覧をどこに持っているか?というと

    • bash・・~/.bash_history
    • zsh・・~/.zsh_history

    とシンプルに隠しファイルとして存在しています。



    そのためファイルの中身を確認するとそのまま履歴が表示されます

    $ cat ~/.zsh_history
    op .
    miss
    git log
    git log$
    history


    つまり

    このファイルから履歴として表示したくない部分を削除すれば history を打っても表示されなくなります



    grepを使って~/.zsh_historyから削除する

    grepを使うと特定の文字列が入っている行を除外する事が簡単に出来ます!

    $ grep -v "git" ~/.zsh_history > ~/.zsh_history_temp

    -vオプションは指定しないパターンに一致しない行のみを出力するオプション



    これによりgitを含まれない行のみが残ったものが「~/.zsh_history_temp」に保存されます

    $ cat ~/.zsh_history 
    op .
    miss
    git log
    git log$
    history
     
    $ cat ~/.zsh_history_temp
    op .
    miss
    history


    インクリメントサーチで検索したコマンドをhistoryから削除する

    本題です!

    インクリメントサーチで検索したコマンドをhistoryから削除するコードです


    シェル得意な方からするとシェルらしくない書き方なんだろうなぁ。。と思いつつ。笑
    ~/zshrc
    # インクリメントサーチで選んだコマンドをhistoryから削除
    alias hd='delete-history-incremental-search'
    ~/.zshrc
    #
    # インクリメントサーチで選んだコマンド検索履歴から削除する
    #
    function delete-history-incremental-search () {
      local SELECTED_COMMAND=$(history -n 1 | fzf)
      # エスケープを押した場合や選択されていない場合は即時リターン
      [ -z "$SELECTED_COMMAND" ] && return
     
      echo -n "delete  \"\e[31m${SELECTED_COMMAND}\e[m\"  command from history?(y/N): "
      if read -q; then
        echo ""
        # historyファイルを念のためバックアップ
        cat ~/.zsh_history > ~/.zsh_history_backup
     
        # 選択したコマンドをhistoryから削除
        # NOTE:
        #  (基本は不要です)  /usr/bin/ を指定しないと alias で設定したgrepが実行されて行数が自動的に入ってしまう
        #  grep -v xxx とすることで、xxxに一致しなかった内容を出力する
        #  また -F をつける事で、$や.のような正規表現対象の文字列もただの文字列として判断しgrepを実行する
        #  新たなに _temp がついたファイルを作成し、最終的に元のファイルに上書きする
        /usr/bin/grep -vF "${SELECTED_COMMAND}" ~/.zsh_history > ~/.zsh_history_temp && mv ~/.zsh_history_temp ~/.zsh_history
        # shellを再起動しhistoryを読み込みなおす
        exec $SHELL
      else
        echo ""
      fi
    }

    これでhdを実行し missgit log$ を消してみると


    historyから無事消えました!! 🎉




    インクリメントサーチには fzf を使っています。

    オプションも多く、見やすく使いやすくて私の手放せないコマンドの1つ。



    ざっくりやっている内容は

    • fzfで削除対象のコマンドをSELECTED_COMMANDに代入(5行目)
    • grepを使って検索したコマンドを除いたhisotryファイルを作成し上書きする(21行目)

    となります。



    grepコマンドについて

    今回の肝となるgrep

    ポイントを何点か記載しておきます。



    /usr/bin/ の指定


    基本はこの設定は不要な方が多いと思います!

    grepコマンドを拡張している人は参考にしてみて下さい



    私の環境だとgrepコマンドに対してaliasを設定しています

    # grepを拡張したripgrepを使用。-n 行数表示, -I バイナリファイル無視
    alias grep="rg -nI"

    そのためgrepを使うと上記alias版のgrepが使われて行数が表示されてしまい、

    historyにうまく渡せないという問題があっため /usr/bin のgrepを利用し回避しました。

    # grepをそのまま使うと検索結果に行数が表示されてしまう
    $ cat ~/.zsh_history | grep "git"
    cat ~/.zsh_history | grep "git"
    3:git log
    4:git log$
    6:~/.zsh_history | grep "git"
    ・・・
     
     
    # /usr/bin/grepを使う事で回避
    $ cat ~/.zsh_history | /usr/bin/grep "git"
    git log
    git log$
    ~/.zsh_history | grep "git"



    -vオプション


    前述した通り、

    -vオプションを使う事で指定しないパターンに一致しない行のみを出力する事が出来ます

    $ grep -v "git" ~/.zsh_history > ~/.zsh_history_temp
     
     
    $ cat ~/.zsh_history 
    op .
    miss
    git log
    git log$
    history
     
    $ cat ~/.zsh_history_temp
    op .
    miss
    history



    -Fオプション


    -F--fixed-stringsオプションの省略記法。

    正規表現の文字列も含め文字列として扱います



    grepは基本的に正規表現を用いる事が可能です

    # log という文字が入っているもの全てを取り出す
    $ cat ~/.zsh_history | /usr/bin/grep "log"
    docker-compose logs
    firebase login
    git log
    git log$
     
    # 正規表現により末尾が log のもののみを取り出す
    $ cat ~/.zsh_history | /usr/bin/grep "log$"
    git log

    -Fをつける事で正規表現の文字も文字列としてみなしてくれます

    # -F をつける事で正規表現の文字列(ここでは$)も関係なく検索が可能
    $ cat ~/.zsh_history | /usr/bin/grep -F "log$"
    git log$
    プロフィールの背景画像 プロフィール画像
    Yuki Takara
    都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。