bashのcdコマンドを拡張する

bashのpushdやpopdコマンドを使いたくても使えない私のような人は、cdコマンドを拡張すれば良い。

はじめに

bashにはpushdとその反対のpopdコマンドが内蔵されている。
pushdは指定されたディレクトリに移動後、移動先のディレクトリをDIRSTACK配列環境変数の先頭の要素として追加する。
popdはDIRSTACKの先頭の要素に保存されているディレクトリに移動後、その要素を削除して2番目の要素以降を先頭に1つずつ詰める。
pushdとpopdは移動先のディレクトリから移動前のディレクトリに戻りたいときに使用すると便利だが、ディレクトリの移動には日常的に使用するcdコマンドを使ってしまうため、popdで戻ろうとしても後の祭りとなることが多い。
そこで、cdコマンドに履歴機能を持たせることで、pushdやpopdよりも使い勝手の良いものを作成する。

スクリプトの作成

~/.bashrcに以下のサブルーチンを追加する。

# 環境設定
# LAST_DIRLIST:最終DIRLIST要素番号(処理の都合上、最大DIRLIST要素数で
#               はない)
# SAVE_DIRLIST:DIRLISTの保存先ファイル名

LAST_DIRLIST=19
SAVE_DIRLIST=$HOME/.dirlist

# ディレクトリの移動
# 使用法:chdir [dir]
# dir:移動先のディレクトリ
#      指定されていない場合はホームディレクトリに移動
# 説明:カレントディレクトリをDIRLIST配列環境変数の先頭の要素として追
#       加し、指定されたディレクトリにカレントディレクトリを移動する。
#       DIRLISTには最大で(LAST_DIRLIST + 1)件の履歴を保存する。

function chdir
{
    if [ ${#DIRLIST[@]} -gt $LAST_DIRLIST ]; then
        unset DIRLIST[$LAST_DIRLIST]
    fi
    DIRLIST=(`pwd` ${DIRLIST[@]})
    cd $*
}

# 過去のディレクトリに移動
# 使用法:unchdir [num]
# num:過去の履歴の番号(1=1つ前、2=2つ前、…)
#      指定されていない場合は1つ前に移動
# 説明:指定された履歴番号(DIRLISTの添え字)の要素をDIRLISTから削除
#       し、カレントディレクトリを先頭の要素として追加して、削除した要
#       素に保存されていたディレクトリにカレントディレクトリを移動す
#       る。

function unchdir
{
    if [ ${#DIRLIST[@]} -eq 0 ]; then
        echo "No changing directory"
        return 1
    fi
    if [ $# -eq 0 ]; then
        i=0
    else
        i=$1
        if [ $i -le 0 ]; then
            i=1
        elif [ $i -gt ${#DIRLIST[@]} ]; then
            i=${#DIRLIST[@]}
        fi
        i=`expr $i - 1`
    fi
    d=${DIRLIST[$i]}
    unset DIRLIST[$i]
    DIRLIST=(`pwd` ${DIRLIST[@]})
    cd $d
}

# 過去のディレクトリに戻る
# 使用法:backdir [num]
# num:過去の履歴の番号(1=1つ前、2=2つ前、…)
#      指定されていない場合は1つ前に戻る
# 説明:先頭から指定された履歴番号(DIRLISTの添え字)までの要素を
#       DIRLISTから削除し、指定された要素に保存されていたディレクトリ
#       にカレントディレクトリを移動する。

function backdir
{
    if [ ${#DIRLIST[@]} -eq 0 ]; then
        echo "No changing directory"
        return 1
    fi
    if [ $# -eq 0 ]; then
        i=0
    else
        i=$1
        if [ $i -le 0 ]; then
            i=1
        elif [ $i -gt ${#DIRLIST[@]} ]; then
            i=${#DIRLIST[@]}
        fi
        i=`expr $i - 1`
    fi
    d=${DIRLIST[$i]}
    DIRLIST=(`awk "BEGIN {for (i = $i + 2; i < ARGC; i++) {\
        print ARGV[i], "\n";}}" ${DIRLIST[@]}`)
    cd $d
}

# ディレクトリの移動履歴を表示
# 使用法:printdir
# 説明:DIRLISTの全要素を履歴番号(DIRLISTの添え字)
#       付きで表示する。

function printdir
{
    echo ${DIRLIST[@]} | awk '{for (i = 1; i <= NF; i++) {\
        printf "[%d] %s\n", i, $(i);}}'
}

# 過去のディレクトリを忘れる
# 使用法:forgetdir [num]
# num:過去の履歴の番号(1=1つ前、2=2つ前、…)
#      指定されていない場合は1つ前を忘れる
# 説明:指定された履歴番号(DIRLISTの添え字)の要素をDIRLISTから削除す
#       る。

function forgetdir
{
    if [ ${#DIRLIST[@]} -eq 0 ]; then
        return 1
    fi
    if [ $# -eq 0 ]; then
        i=0
    else
        i=$1
        if [ $i -le 0 ]; then
            i=1
        elif [ $i -gt ${#DIRLIST[@]} ]; then
            i=${#DIRLIST[@]}
        fi
        i=`expr $i - 1`
    fi
    unset DIRLIST[$i]
    DIRLIST=(${DIRLIST[@]})
}

# ディレクトリの移動履歴を保存
# 使用法:savedir
# 説明:DIRLISTの全要素をSAVE_DIRLISTに設定されているファイルに保存す
#       る。

function savedir
{
    d=(`pwd` ${DIRLIST[@]})
    echo ${d[@]} > $SAVE_DIRLIST
}

# ディレクトリの移動履歴を復旧
# 使用法:restoredir
# 説明:DIRLISTの全要素をSAVE_DIRLISTに設定されているファイルの内容で
#       置き換える。

function restoredir
{
    if [ -f $SAVE_DIRLIST ]; then
        DIRLIST=(`cat $SAVE_DIRLIST`)
        backdir
    fi
}

# シェル終了時にディレクトリの移動履歴を保存
# 使用法:exit_savedir
# 説明:exitコマンドを置き換えることで、シェル終了時にディレクトリの移
#       動履歴を保存する。

function exit_savedir
{
    savedir
    exit
}

エイリアスの設定

~/.bashrcに以下のエイリアスを追加する。
ただし、上記「スクリプトの作成」に記載したサブルーチンの後に追加しないと、例えば、chdirが実行するcdがchdirに置き換えられるため、無限にchdirを呼び出すことになり、その結果、スタックオーバーフローが発生する。

# 現在のcdコマンドの拡張など
alias cd="chdir"
alias ud="unchdir"
alias bd="backdir"
alias pd="printdir"
alias fd="forgetdir"
alias exit="exit_savedir"