cdによるディレクトリ移動を効率化


##はじめに
私はいつも`bash`を使っています。
`cd`でディレクトリに移動を行なっていますが、この移動がいつも非効率だなと思っていました。
`cd -`で直前のディレクトリには戻れますが、それ以前に移動したディレクトリに再び移動したい場合は相対パスでもう一度入力し直さなければいけないのでだるいです。
一時期`pushd`と`popd`を利用していた時がありましたが、慣れなかったので今は`cd`による移動のみです。
今回はこの`cd`による移動を効率化したいと思います。

##	pushd・popd・dirs
今回の効率化を実装するにおいて重要なコマンドが3つあります。
**1. pushd**
**2. popd**
**3. dirs**
上記のコマンドを使って`.bashrc`を編集したいと思います。
念のためちょっとこのコマンドの紹介をしたいと思います。

**pushd**
`cd`コマンドと同じく引数で指定したディレクトリに移動することが出来るコマンドです。この時、移動前のカレントディレクトリをスタックに保存して指定されたディレクトリに移動するというのが`pushd`の特徴です。

**popd**
`pushd`コマンドによってスタックに保存された最新ディレクトリに移動するのが`popd`コマンドです。スタックという構造なので後入先出法になります。

**dirs**
`pushd`コマンドによって保存されているスタック情報を確認出来るのが`dirs`コマンドです。スタックのクリアなどもこのコマンドで実行することが出来ます。

ではこれらのコマンドを使って`cd`コマンドの効率化をしていきましょう。

##cdの情報をスタックに保存（cdコマンドの再定義）
本来の`cd`コマンドでは、移動前のカレントディレクトリ情報を保存する機能はありません。そこで`cd`コマンドを`pushd`コマンドで再定義します。
上記に以前`pushd`と`popd`を利用していたと書きましたが、5文字も打ってられない！めんどくさい！という理由からやめてしまいました。今思えばエイリアスしとけばそっちで慣れたかもしれないです。再定義しておけば同じ機能をいつもの`cd`コマンドで実現出来ます。
更に今回は移動履歴に重複がある場合余計にスタックに保存しないようになっています。
コードは以下のとおり。

```bash:.bashrc
# 履歴を記録するcdの再定義（pushdの利用）
function cd {
    if [ -z "$1" ] ; then
        # cd 連打で余計な $DIRSTACK を増やさない
        test "$PWD" != "$HOME" && pushd $HOME > /dev/null
    else
        pushd "$1" > /dev/null
    fi  
}
```

##cdの履歴を遡ったディレクトリ移動
`cd`コマンドを再定義したので`cd`の移動履歴がスタックに保存されるようになります。次はこの履歴を一覧表示し遡りたいディレクトリを番号で指定するとそのディレクトリに移動出来るようにします。
コードは以下のとおり。

```bash:.bashrc
# cdの履歴の中から移動したいディレクトリを選択するcd historyの定義
function cdh {
    local dirnum
    dirs -v | sort -k 2 | uniq -f 1 | sort -n -k 1 | head -n $(( LINES - 3 ))
    read -p "select number: " dirnum
    if [ -z "$dirnum" ] ; then
        echo "$FUNCNAME: Abort." 1>&2
    elif ( echo $dirnum | egrep '^[[:digit:]]+$' > /dev/null ) ; then
        cd "$( echo ${DIRSTACK[$dirnum]} | sed -e "s;^~;$HOME;" )"
    else
        echo "$FUNCNAME: Wrong." 1>&2
    fi
}
```

##cdの履歴を遡るディレクトリ移動
`cd history`で過去のディレクトリを指定して移動出来るようになりましたが、ただ単に遡りたい時用に`popd`コマンドを利用して実現できる`cd back`を紹介します。
コードは以下のとおり。

```bash:.bashrc
#cd履歴を辿るcd backの定義（popdの利用）
function cdb {
    local num=$1 i
    if [ -z "$num" -o "$num" = 1 ] ; then
        popd >/dev/null
        return
    elif [[ "$num" =~ ^[0-9]+$ ]] ; then
        for (( i=0 ; i<num ; i++ )) ; do
            popd >/dev/null
        done
        return
    else
        echo "cdback: argument is invalid." >&2
    fi
}
```

##入力がめんどうなディレクトリへの簡易移動
ディレクトリ名に日本語を使っていると`cd`で移動するとき厄介ですよね。いつもむしゃくしゃしてます。また、入力しずらい面倒なディレクトリ名だった場合もありますよね。そんなとき、カレントディレクトリの一覧を表示して番号指定で移動出来るように`cd list`を定義しました。
コードは以下のとおり。

```bash:.bashrc
# カレントディレクトリの中にあるディレクトリを番号指定で移動するcd listの定義
function cdl {
    local -a dirlist opt_f=false
    local i d num=0 dirnum opt opt_f
    while getopts ":f" opt ; do
        case $opt in
            f ) opt_f=true ;;
        esac
    done
    shift $(( OPTIND -1 ))
    dirlist[0]=..
    for d in * ; do test -d "$d" && dirlist[$((++num))]="$d" ; done
    for i in $( seq 0 $num ) ; do printf "%3d %s%b\n" $i "$( $opt_f && echo -n "$PWD/" )${dirlist[$i]}" ; done
    read -p "select number: " dirnum
    if [ -z "$dirnum" ] ; then
        echo "$FUNCNAME: Abort." 1>&2
    elif ( echo $dirnum | egrep '^[[:digit:]]+$' > /dev/null ) ; then
        cd "${dirlist[$dirnum]}"
    else
        echo "$FUNCNAME: Something wrong." 1>&2
    fi
}

```

##おわりに
これでかなりディレクトリ移動が早くなると思います。
皆さんにも是非やってみてください。参考までにどうぞ。
今回は以下のサイトを参考にさせて頂きました。ありがとうございます。
こちらのサイトのほうが細かいソースの内容を説明しているので、確認したい人はこちらの記事も是非確認してみてください。

>いかに素早く目的のディレクトリにcdするか、そのためのTips
>http://qiita.com/xtetsuji/items/31bc53e92d94b1602b5d