golangの日記

Go言語を中心にプログラミングについてのブログ

コマンド履歴の検索とかディレクトリ移動について(Bash)

shellscript.png


【Shell入門】一生触るターミナル操作を強化しておこうぜ! この動画がよかったので、自分のコマンド履歴の設定とか検索とかディレクトリ移動について(Bash)





目次



history 履歴について


履歴に関する設定(.bashrc)

# コマンドの先頭が空文字の場合と重複したコマンドは記録しない
HISTCONTROL=ignoreboth:erasedups

# 履歴の保存サイズ
HISTSIZE=100000
HISTFILESIZE=100000

# 前方一致検索。Ctrl+n か Ctrl+p で履歴をたどるときに、
# 例えば ls と打ってから Ctrl+p すると ls の履歴のみたどれる
bind '"\C-n": history-search-forward'
bind '"\C-p": history-search-backward'


Ctrl + r の強化

peco (fzfでもいいけど) 使って Ctrl + r の履歴検索を強化する。リリースページ からバイナリダウンロードしてもいいし clone して go build して /usr/local/bin に放り込んでもいいけど apt が楽なので

sudo apt install peco


インストールしたら .bashrc とかに以下を記述して Ctrl+r の履歴検索を強化する。これも --query $READLINE_LINE で途中まで入力した文字で前方一致検索できる

func_peco_history() {
    local v=$(HISTTIMEFORMAT= history | sed -E s/^\ *[0-9]\+\ \+// \
        | sort -r | uniq | peco --query "$READLINE_LINE")
    READLINE_LINE="$v"
    READLINE_POINT=${#v}
}
bind -x '"\C-r": func_peco_history'



ディレクトリの移動について


ディレクトリ移動はコマンド履歴の検索と以下の2つの方法を使う。よく使うパスが長いディレクトリなんかは alias とか設定しとけばいいと思う。


ディレクトリの検索

peco を使って find コマンドの結果から絞り込む。.bashrc とかに以下を書いて pcd する。フィルタを Regexp にしてるのは project$ のように $ を付けるとそれ以下の階層を除外できてよい。使用頻度少ないけど

pcd() {
    local c="."
    [ -n "$1" ] && c="$1"
    local v=$(find $c -type d -and ! -regex '.*/node_modules/.*' 2> /dev/null | peco --initial-filter=Regexp)
    [ -d "$v" ] && cd "$v"
}


cd コマンドの強化

間違いなく この記事 だと思うけど、その昔に読んでからずっと使ってる便利な cd コマンド拡張

func_enhanced_cd() {
    local x2 the_new_dir adir index
    local -i cnt

    if [[ $1 == "--" ]]; then
        dirs -v
        return 0
    fi

    the_new_dir=$1
    [[ -z $1 ]] && the_new_dir=$HOME

    if [[ ${the_new_dir:0:1} == '-' ]]; then
        #
        # Extract dir N from dirs
        index=${the_new_dir:1}
        [[ -z $index ]] && index=1
        adir=$(dirs +$index)
        [[ -z $adir ]] && return 1
        the_new_dir=$adir
    fi

    #
    # '~' has to be substituted by ${HOME}
    [[ ${the_new_dir:0:1} == '~' ]] && the_new_dir="${HOME}${the_new_dir:1}"

    #
    # Now change to the new dir and add to the top of the stack
    pushd "${the_new_dir}" > /dev/null
    [[ $? -ne 0 ]] && return 1
    the_new_dir=$(pwd)

    #
    # Trim down everything beyond 11th entry
    popd -n +11 2> /dev/null 1> /dev/null

    #
    # Remove any other occurence of this dir, skipping the top of the stack
    for ((cnt = 1; cnt <= 10; cnt++)); do
        x2=$(dirs +${cnt} 2> /dev/null)
        [[ $? -ne 0 ]] && return 0
        [[ ${x2:0:1} == '~' ]] && x2="${HOME}${x2:1}"
        if [[ "${x2}" == "${the_new_dir}" ]]; then
            popd -n +$cnt 2> /dev/null 1> /dev/null
            cnt=cnt-1
        fi
    done

    return 0
}
alias cd=func_enhanced_cd


使い方

  1. コマンドに -- を付けると、最近 cd した履歴が表示される

     $ cd --
     0 ~/foo
     1 ~/bar
     2 ~/baz
    
  2. 履歴の左にある番号を -1 のように指定することで cd できる

     $ cd -1
    



edit-command-line の自作


冒頭の動画見てて zshedit-command-line というのが便利そうだったので bash でもやりたいと思って作った。 alt か esc キーのあとに e を押せば書きかけのコマンドを vim で編集できる。

func_edit_command_line() {
    # 何か入力してないと起動しない
    [ -z "$READLINE_LINE" ] && return 0
    # EDITOR 変数が空なことがあるのか知らんけど一応
    [ -z "$EDITOR" ] && EDITOR='/usr/bin/vim'
    # /tmp ディレクトリに一時ファイルを作ってそれを編集
    local RAMDOM_STR=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)
    local TMP_FILE="/tmp/$RAMDOM_STR"
    echo "$READLINE_LINE" > "$TMP_FILE" && $EDITOR "$TMP_FILE"

    if [ $? -ne 0 ]; then
        [ -f "$TMP_FILE" ] && rm -f "$TMP_FILE"
        return $?
    fi

    local TMP_STR=$(cat $TMP_FILE)
    if [ -n "$TMP_STR" ]; then
        # エディタで編集した内容を戻す
        READLINE_LINE="$TMP_STR"
        READLINE_POINT="${#READLINE_LINE}"
    fi

    [ -f "$TMP_FILE" ] && rm -f "$TMP_FILE"
    return 0
}

bind -x '"\ee": func_edit_command_line'


書き終わってから何となく bind -l で関数見たら edit-and-execute-command とかある。bind -P 見たら \C-x\C-e とある。つまり Ctrl+x Ctrl+e で同じことできる。エディタ閉じたら即実行されるとか違いはあるけど