前回はココアさんがpecoをチュートリアルしてくれました。
さて、さらにcdコマンドを弄っていきます。
多分、これで最後だと思います。
第6羽:cdコマンドはログを取り、pecoを駆りて履歴の山を行く
今回は、一度行ったディレクトリを独自に記録してpecoで選択、移動できるようにしてみます。
$ cd -
上記を実行すると$OLD_PWD
という一つ前に居たパスまで戻ります。
でも、WindowsさんのGUIは←アイコンを使うともっと前まで戻れますよね?
これが今回のモチベーションです。
機能
- 履歴検索機能
- 履歴整理機能
履歴検索機能についての仕様
-
cd -
の機能を置き換えること
cd -
が不便ですし、同じ方向の機能なので共存させる意味はないと判断しました。 -
絶対パスで記録すること
絶対パスの方がcdしやすいですしね
履歴整理機能についての仕様
-
並びは弄らないこと
ログをpecoするときに新しい順番に並べる仕様を壊さないため -
重複履歴は新しいモノのみを残して削除すること
重複して表示しない機能はありますが、一応 -
どこかのタイミングで自動実行されること
ユーザーが気にしなければならないとかゴミなので自動実行です。
こんな感じになった
# 環境変数が定義されているかを確認
if [[ ! -v LOG_DIR ]]; then
# パスは自由
export LOG_DIR=${HOME}
fi
# ログディレクトリの存在チェック
if [[ ! -d LOG_DIR ]];then
# ログディレクトリを一気に作成
mkdir -p ${LOG_DIR}
fi
# ファイル名は強制固定
CD_HISTORY=${LOG_DIR}/cd_history.log
custom_cdls()
{
local -r argc=$#
local destination=$*
case ${argc} in
0)if type peco &> /dev/null; then
local asc_order='sort -f'
destination=$(find ./ -maxdepth 1 -mindepth 1 -type d | eval $asc_order | peco)
fi
;;
1)destination=$1
if type peco &> /dev/null; then
# pecoを持ってるときだけ「-」機能を置き換える
if [[ ${destination} == '-' ]];then
local trim_duplication='awk '\''!dictionaty[$0]++'\'''
local reverse_order='tac'
destination=$(\cat ${CD_HISTORY} \
| eval ${reverse_order} \
| eval ${trim_duplication} \
| peco) # Cannot use --query option
fi
fi
;;
esac
# Don't move $HOME
if [ -z $destination ]; then
echo 'Missing args';
return 1;
fi
# \cd => builtin cd
\cd ${destination}
if [ $? -ne 0 ]; then
return $?
fi
clear && ls
# カレントパスを保存
pwd >> ${CD_HISTORY}
}
alias cd='custom_cdls'
_clean_dir_history()
{
local trim_duplication='awk '\''!dictionaty[$0]++'\'''
local reverse_order='tac'
# 履歴ファイルを読み込み
uniq_ary=($(cat ${CD_HISTORY} \
| eval $reverse_order \
| eval $trim_duplication \
| eval $reverse_order))
# 一応バックアップしておく
\cp $CD_HISTORY $CD_HISTORY.bak &> /dev/null
# ファイル内容を全消し
:> ${CD_HISTORY}
for line in "${uniq_ary[@]}"; do
if [[ -e $line ]]; then
# パスが存在していたら書き込む
echo $line >> ${CD_HISTORY}
fi
done
}
# 実行
_clean_dir_history
スクリプト解説
まず、履歴ファイルのパスを環境変数として持っています。
外からユーザーが環境変数を定義してくれてれば再定義せずにそれを使うようにしています。
自由なところに履歴ファイルを置いてください。
デフォルトだと$HOMEに置くことになります。
また、そこまでのパスが見つからなかった場合はmkdir -p
で一気に作っています。
sudoを要求されるパスは想定していないので気を付けてください。
履歴検索機能
前提として、引数の個数判定で1つだった場合の処理に該当処理を加えています。
引数が「-」だけだったときに処理を差し替えます。
-
cat ${CD_HISTORY}
catでファイルの内容を次に流しているだけです。 -
reverse_order
最近のモノを一番前に出すために逆転を行います。
前とかの記事ではコマンドを持ってるか確認していましたが、メンドウになって辞めてしまいました。
みなさん、コマンドがあるかどうかなんてチェックします?また、どの程度までチェックするんでしょうか? 最初に逆転させておかないと最新のモノを残して重複を削除できないので注意してください。
-
trim_duplication
履歴に重複する内容があった場合に、同じ候補を出してしまうのは混乱の元なので重複を削除します。
awkコマンドを用いて辞書式配列を作ることで重複削除を実現しています。
-
pwd >> ${CD_HISTORY}
移動した最後の処理に履歴ファイルに現在のパスを追記します。
最初はreadlink
コマンドなどを使っていましたが、Macではツライとの話を聞いて考え直したところ
シンプルに「移動後にカレントパスをファイルに流す」で解決しました。
もっと上手い解決があればご教授ください。
履歴整理機能
自動実行タイミングを計るのをスクリプト内で完結させるのは大変メンドクサイことに気付いたので、bashが実行されたときに整理するようにします。
それだとタイミングの問題も起きなさそうですしね!
.bashrcが読み込まれた際に実行されるようにするので、別に関数化する必要はないんですけども、何してるのか明確にするために関数化したので、最後に関数を呼び出して実行しています。
-
cat ${CD_HISTORY}
ログファイルを読み込んでパイプに流していきます。
この状態はまだ、昇順(時系列順)です。 -
reverse_order
この降順化処理は「重複する場合は最近のモノを残す」という仕様を満たすために行います。
この状態では新しい記録が上になっています。 -
trim_duplication
新しいモノが上に来ている状態で、上から処理して重複を削除します。
これで重複しているログは、最近のログだけが残るようになります。 -
reverse_order
ログの順列が降順(逆時系列順)になっています。
このままログファイルに書き出してしまうと時系列が逆転してしまいます。
最後に時系列を元に戻すためにもう一度順序を逆転させます。 -
for line in "${uniq_ary[@]}";
lineに一行ずつ入れて、存在チェックした後にログに追記していくだけで特に書くようなことはないです。
ただ、for文の@マークがクセのようで、@と*でほぼ同機能らしいですが、@は空白で区切るのに対し、*はIFSで区切る違いがあるので今後注意が必要かもしれません。
最後に
Windowsの戻るコマンド(?)は一つ以上前に戻れるってのがモチベーションだった訳ですけど、
全然違う機能が出来上がってんじゃん!
「こまけぇこたぁいいんだよ!!」AA略