cdの引数を絶対パスにしてコマンドヒストリに残す

cdの引数が相対パスのままコマンドヒストリに残って便利な例が思いつかないので、絶対パスでコマンドヒストリに残すようにする。 具体的には、以下のシェル関数を.bashrcに書く。

if [[ -n "$PS1" ]]; then
    cd() {
        command cd "$@"
        local s=$?
        if [[ ($s -eq 0) && (${#FUNCNAME[*]} -eq 1) ]]; then
            history -s cd $(printf "%q" "$PWD")
        fi
        return $s
    }
fi

いくつかの重要なポイントを以下に記す。

  • cdの定義を上書きしているが、このような場合中で普通にcdを呼ぶと再帰してしまうためcommand組み込みコマンドを使う。
  • "$@"の代わりに"$1"を使うことはできない。cdを引数なしで呼んだときホームディレクトリに移動しなくなってしまう。
  • cdの成否は戻り値で判定できる。
  • history -sは直前のヒストリを上書きするが、関数の中でcdが呼ばれたときは元の関数をヒストリに残したいので実行させない。
  • 配列変数FUNCNAMEがコールスタックになっており、この配列の要素数が1かどうかでシェルから直接呼ばれているか(関数の中からでないか)判定できる。
  • 文字列のquoteにはprintf組み込みコマンドを使う。%qでquoteされた文字列が得られる。

Ctrl+Rによるヒストリ検索からディレクトリ移動できるようになって便利。

$ cd tmp/

$ cd space\ and\ meta\'\"\\characters/

$ cd ..

$ cd

$ history | tail
 (snip)
 5033  cd /home/user/tmp
 5034  cd /home/user/tmp/space\ and\ meta\'\"\\characters
 5035  cd /home/user/tmp
 5036  cd /home/user
 5037  history | tail

追記(2014-12-11)

cdの戻り値を正しく返すように修正した。

追記(2014-12-11)

インタラクティブシェルとして起動したときのみ定義することをコードとして明記した。