読者です 読者をやめる 読者になる 読者になる

たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

最強のcdコマンドを目指して

bash シェルスクリプト

cdで無限の可能性を

集え!OITer!!! OIT Advent Calender 2016!!!!

この記事は、 OIT Advent Calendar 2016の5日目の記事です。

5日目の今日も隊長がお送りします。


皆さんcdコマンド使ってますか。
cmdにもシェルにもふつーにありますよね。
lsとかとならんでめちゃ使うコマンドの一つだと思います。

よく使うのでcdは快適であってほしいです。
lsコマンドはオプションが沢山あり、よく使うls -aとかls -lなんかはエイリアス登録して使ってる人も多いんじゃないですか?
しかしcdmanを見ると・・・

http://ss64.com/bash/cd.htmlss64.com

オプションが二つしかありません。(そのうち一つはデフォルトでON)

これってこれ以上快適にできないってことじゃん!!!!

そうです。

ぼくのかんがえたさいきょうのcdこまんど その名は cdx

  • 履歴がある
  • 表示がカッコいい
  • ブックマークがある
  • ディレクトリが無かったら作るか訊く
  • ひとつ前にいたディレクトリにすぐもどれる
  • 探し物してるときに自動でlsしてくれる

_人人人人人人人人人人人人_
> そんなんありません! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

(たぶん)

なら自分でつくろうか

cdコマンドだけでも全く問題はないんですが
僕みたいに車輪の再発明をした後、その車輪にスパイクとか発光するやつとかをつけちゃうような奴はこういうことが大好きです。
まぁcdxみたいなのはないと思うので車輪の再発明にあたるかは微妙ですが・・・


(2016/12/05 13:55 追記)cdxみたいなのありました・・・!こっちのがすごそうです! qiita.com (追記終わり)


車輪の再発明(しゃりんのさいはつめい、英: reinventing the wheel)は、車輪を題材にした慣用句であり、世界中で使われている。「広く受け入れられ確立されている技術や解決法を知らずに(または意図的に無視して)、同様のものを再び一から作ること」を意味する。 -車輪の再発明 Wikipedia

使う言語はbashです。

履歴機能

まずは cdx に履歴を付けます。
履歴はいつでも参照できて、番号指定でそこへ移動できるようにします。
パスをどこかファイルへechoしておいてそれを読み出すようにします。

if [ $cdx_option == "-h" ]; then
    line=$2
    if [ "$line" == "" ]; then
            line=`cat ~/.cdx_history|wc -l`
            lineStart=`echo $line - 10|bc`
        if [ $lineStart -lt 1 ]; then
            lineStart=1
        fi
        cat -n ~/.cdx_history|sed -n "$lineStart,${line}p"
        cd_flag=0
    elif [ $line == "clear" ]; then
        echo "" > ~/.cdx_history
    else
        change_to=`cat ~/.cdx_history | sed -n "$line,${line}p"`
    fi
fi
###中略###
#cdするまえに
echo `pwd` >> ~/.cdx_history

実はC#Jsonを使って書いたんですけど思いのほか長くなってしまった・・・のでやめました・・・。
これで~/.cdx_historyにどんどん履歴がたまっていきます。

cdx.sh -h 1

とすると履歴ファイルの1行目へ移動します。

cdx.sh -h

とだけ打ち込むと最新の履歴10件を表示して終了します。
無限に履歴をため込むのでときどきcdx -h clearとしてやるといいですね。
cdx5
(エンコードの調子が悪い)

表示をかっこよくする

ぼくはまだ中二病を卒業できてないところがあるので表示がカッコいいとテンションが上がります。
なのでcdした後に単に移動先のディレクトリをpwdするだけじゃなくて色々表示しちゃいます。

if [ $cd_flag = 1 ]; then
    bef_dir=`pwd`
    cd $change_to > /dev/null
    if [ $? = 1 ]; then
        echo -e -n "${change_to}\nが見つかりませんでした。\n作ろうか?(y/n):"
        read ans
        if [ $ans == 'y' ]; then
            mkdir $change_to
            cd $change_to > /dev/null
        fi
    else
        echo -e "  ${clr_Black}--> ${clr_main}cdx ${clr_Black}: ${clr_green}$bef_dir${clr_Black} ->>>${clr_green} `pwd`${clr_reset} "
    fi
fi

さっきの部分のelse側はcdに成功した場合に通りますのでそこでカッコイイ表示を出してやります。
これがカッコよく感じるかは個人差があるのであれですが・・・

ブックマーク機能

次はブックマークを作ります。
いつもアクセスするディレクトリとかにはできるだけ短いキーストロークで移動したいもんです。
たとえば

cdx 0 #ブックマークの0番目に移動

とこんな具合に

まずブックマークは ~/.bashrcに記述するようにします。
こんな感じで

#~/.bashrc 
#cdx bookmark
export cdx_bookmark=("ブックマーク0" "ブックマーク1")

環境変数cdx_bookmarkを作成してそこにブックマークしておきたいディレクトリを記述します。

次にcdx.shにこれを読み出してインデックス通りに移動するように追記します。

#!/bin/bash

## cdx is hyper cd command

##color setting
clr_error="\e[1;31m"
clr_main="\e[1;35m"

cdx_bm_arg=$1
cd_flag=1
change_to=$HOME

if expr "$cdx_bm_arg" : "[0-9]+" > /dev/null  ; then
        if [ $cdx_bm_arg -ge ${#cdx_bookmark[@]} ] || [ $cdx_bm_arg -lt 0 ]; then
                echo -e "${clr_error}[ブックマークの範囲外です]"
                cd_flag=0
        fi
        #読み出し
        change_to=${cdx_bookmark[$cdx_bm_arg]}
else
        #引数のところへcd
        change_to=$cdx_bm_arg
fi

if [ $cd_flag = 1 ]; then
        cd $change_to > /dev/null
fi

これで. ./cdx.sh 0とかするとcdx_bookmarkの0番目を読み出してcdできるようになります。
でもこれだけだとなんか寂しいのでもう少し拡張します。

#!/bin/bash

#cdxの補完
_cdx_complete(){
    case $2 in
    [0-9]* )
        COMPREPLY=( `echo "${cdx_bookmark[$2]}"` )
        ;;
    *)
        ;;
    esac
}
complete -d -F _cdx_complete cdx

これは cdx の補完を行う記述です。Tabキー押したら発動するあれです。
cdx 0 としたとき 0 の部分に実際のパスを展開してくれます。
cdx1
こんな感じで
※書いただけでは使えません。

さらに 一時ブックマーク みたいなのも作りましょう。

$cdx_option=$1
if [ $cdx_option == "bm" ]; then
        export CDX_TMP_BM=`pwd`
        echo -e "${clr_green}Temporary BookMark${clr_Black} <-- ${clr_green}`pwd`"
        cd_flag=0
fi
if [ $cdx_option == "b" ]; then
        change_to=$CDX_TMP_BM
        cd_flag=1
fi

こうすると「きょうの作業ディレクトリはね~ここなの!」とcdx bmしてやれば
どこかに移動しててもcdx bですぐに戻れます。
cdx3

移動先のディレクトリが無かったら作るか訊く

これできたら結構うれしいですよね?
cdコマンドが「そのようなファイルやディレクトリはありません(冷酷)」とか言ってくると
無いんじゃなくてつくるんだよ!って感じます。
それでは

if [ $cd_flag = 1 ]; then
    cd $change_to > /dev/null
    if [ $? = 1 ]; then
        echo -e -n "${change_to}\nが見つかりませんでした。\n作ろうか?(y/n):"
        read ans
        if [ $ans == 'y' ]; then
            mkdir $change_to
            cd $change_to > /dev/null
        fi
    fi
fi

cdx.shの最後のif文の中をこういう風に変えます。
読んだら分かりますがcdに失敗したら訊いて作って移動します。great
cdx4

ひとつ前にいたディレクトリにすぐもどれる

pushdpopdというコマンドがあるのを知ってますか?
これpush,popってところから想像つくと思うんですけど、移動するときにスタックにパスを残しておけるんです。
なのでpopdを使えば一瞬でひとつ前のディレクトリに移動できるわけです。

しかし
みなさんここでお手元のキーボードをご覧ください。
無い方のために画像用意しました。
keyboard
試しにpopdって打ち込んでみてください。

やりました?

結構キーストローク長くないですか??え?そうでもない?
ならpushdはどうですか?

うん長いね?
それにディレクトリ変えたいだけなのにいくつもコマンドあるとか無くない?

なのでcdxに含んでしまいましょう。
といってもさっきまでのスクリプト内のcdpushdに変えて、popdcdx -pという形で呼び出せるようにするだけです。

if [ $cdx_option == "-p" ]; then
    bef_dir=`pwd`
    popd
    if [ $? = 1 ]; then
        echo -e "  ${clr_Black}--> ${clr_main}cdx ${clr_Black}: ${clr_green}$bef_dir${clr_Black} ->>>${clr_green} `pwd`${clr_reset} "
    fi
    cd_flag=0
fi

正直cdx -pってpopdよりなげーじゃんって思ってますけどいいんです。
ひとつにまとまっている。これが重要です。

そしてこれpopdってコマンドラインから打ち込んでもちゃんとひとつ前のディレクトリに移動できます。(当然だけど)

探し物してるときに自動でlsしてくれる

皆さんLinux上で探し物するときcdlscdlscdls→・・・ってしません?。僕はします。
探し物をしているときっていうのはcdlsというステップを踏みます。踏め!
なので何回目か以降はcdxが自動的にlsしてくれるようにします。
さて、探し物しているかどうかをcdxは判断しないとダメなんですが・・・それには~/.bash_historyを使います。

history -a
line=`cat ~/.bash_history|wc -l`
lineStart=`echo $line - 6|bc`
if [ $lineStart -lt 1 ]; then
    lineStart=1
fi
if [ `cat ~/.bash_history|sed -n "$lineStart,${line}p"|grep "ls"|wc -l` -ge 2 ]; then
    echo -e "${clr_main}探し物ですか?${clr_reset}"
    ls -la
fi

pushdに成功した後のelseにこれを突っ込みました。
まず、~/.bash_historyを更新します。その次に~/.bash_historyの下から6行を見てlsが2つ以上あればls -laを実行します。
なんだかハイテクですよね。
cdx6
(エンコードの調子わる)

作ったコマンドを使えるようにする

このままでは呼び出すときに

. [cdx.shまでのパス] [もろもろの引数]

としないと使えません。
cd系のコマンドをスクリプト内で使ってもスクリプト内しかディレクトリが変わらないのでsource(略系は .)コマンドで反映するようにします。
でもこれ 明らかに面倒

なので~/.bashrcをごにゃごにゃします。

#~/.bashrc

cdx()
{
    . [cdx.shまでのパス] $@
}
. [complete.shまでのパス]

関数として定義してやればいいのです。
さらにcomplete.shも同時にsourceしてやれば補完ができるように!

うーんgreat

最後に

いかがでしたか?cdx、魅力的なコマンドになったと思いませんか?
今回ぼくはcdxをこの記事に合わせて作り直しました。(作り直す前のはここ
結構気に入っててふつーに使ってます。みんなも使ってくれたらうれしいです。

一応オプションのこととかまとめておきます。

オプション 引数 説明
-p なし popdが呼ばれる
-h なし 最新の移動履歴10件が表示される
-h 番号 番号の履歴へpushdする
なし なし $HOMEへpushdする
なし パス パスへpushdする
b なし 一時ブックマークへ飛ぶ
bm なし カレントディレクトリを一時ブックマークする
なし 番号 ブックマークを読みだしてpushdする

シェルスクリプトはかなり自由なのでホントいろいろできます。既存のコマンドもこうすればよりよくなります。
それに書き方も単純です。型がないですし楽です。Cが書けてggれる人なら簡単に書けますYO!

今回作ったcdxコマンドはここにあります。ぷるりくもお待ちしてる。
github.com
適当にgit cloneしたりzipとかでもってきて↑のように~/.bashrcをごにゃごにゃすれば使えるようになります。

それでは良いcdxライフを!