cdで無限の可能性を
集え!OITer!!! OIT Advent Calender 2016!!!!
この記事は、 OIT Advent Calendar 2016の5日目の記事です。
5日目の今日も隊長がお送りします。
皆さんcdコマンド使ってますか。
cmdにもシェルにもふつーにありますよね。
lsとかとならんでめちゃ使うコマンドの一つだと思います。
よく使うのでcdは快適であってほしいです。
lsコマンドはオプションが沢山あり、よく使うls -aとかls -lなんかはエイリアス登録して使ってる人も多いんじゃないですか?
しかしcdのmanを見ると・・・
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
echo `pwd` >> ~/.cdx_history
実はC#でJsonを使って書いたんですけど思いのほか長くなってしまった・・・のでやめました・・・。
これで~/.cdx_historyにどんどん履歴がたまっていきます。
cdx.sh -h 1
とすると履歴ファイルの1行目へ移動します。
cdx.sh -h
とだけ打ち込むと最新の履歴10件を表示して終了します。
無限に履歴をため込むのでときどきcdx -h clearとしてやるといいですね。

(エンコードの調子が悪い)
表示をかっこよくする
ぼくはまだ中二病を卒業できてないところがあるので表示がカッコいいとテンションが上がります。
なので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
とこんな具合に
まずブックマークは ~/.bashrcに記述するようにします。
こんな感じで
export cdx_bookmark=("ブックマーク0" "ブックマーク1")
環境変数cdx_bookmarkを作成してそこにブックマークしておきたいディレクトリを記述します。
次にcdx.shにこれを読み出してインデックス通りに移動するように追記します。
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
change_to=$cdx_bm_arg
fi
if [ $cd_flag = 1 ]; then
cd $change_to > /dev/null
fi
これで. ./cdx.sh 0とかするとcdx_bookmarkの0番目を読み出してcdできるようになります。
でもこれだけだとなんか寂しいのでもう少し拡張します。
_cdx_complete(){
case $2 in
[0-9]* )
COMPREPLY=( `echo "${cdx_bookmark[$2]}"` )
;;
*)
;;
esac
}
complete -d -F _cdx_complete cdx
これは
cdx
の補完を行う記述です。Tabキー押したら発動するあれです。
cdx 0
としたとき
0
の部分に実際のパスを展開してくれます。

こんな感じで
※書いただけでは使えません。
さらに 一時ブックマーク みたいなのも作りましょう。
$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ですぐに戻れます。

移動先のディレクトリが無かったら作るか訊く
これできたら結構うれしいですよね?
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

ひとつ前にいたディレクトリにすぐもどれる
pushdとpopdというコマンドがあるのを知ってますか?
これpush,popってところから想像つくと思うんですけど、移動するときにスタックにパスを残しておけるんです。
なのでpopdを使えば一瞬でひとつ前のディレクトリに移動できるわけです。
しかし
みなさんここでお手元のキーボードをご覧ください。
無い方のために画像用意しました。

試しにpopdって打ち込んでみてください。
やりました?
結構キーストローク長くないですか??え?そうでもない?
ならpushdはどうですか?
うん長いね?
それにディレクトリ変えたいだけなのにいくつもコマンドあるとか無くない?
なのでcdxに含んでしまいましょう。
といってもさっきまでのスクリプト内のcdをpushdに変えて、popdをcdx -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上で探し物するときcd→ls→cd→ls→cd→ls→・・・ってしません?。僕はします。
探し物をしているときっていうのはcd→lsというステップを踏みます。踏め!
なので何回目か以降は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を実行します。
なんだかハイテクですよね。

(エンコードの調子わる)
作ったコマンドを使えるようにする
このままでは呼び出すときに
. [cdx.shまでのパス] [もろもろの引数]
としないと使えません。
cd系のコマンドをスクリプト内で使ってもスクリプト内しかディレクトリが変わらないのでsource(略系は .)コマンドで反映するようにします。
でもこれ 明らかに面倒
なので~/.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ライフを!