ターミナル操作の8割はcdとlsコマンドで操作するとかいう記事をどっかで見かけたような見かけないような。
ディレクトリ移動するだけなのに、パス名を思い出すだけなのにそんなに作業時間かけたくないですよね?
bashではcd
tabでカレントディレクトリ一覧が表示されたり、パスの途中まで打ち込んでいれば残りを補完してくれます。
zshやfishではもっと強力な補完機能がついてますが、ここでは語りません。
cdするときに3階層も4階層も下りたいけど、途中の1階層、2階層の名前が思い出せない、なんてこと、私はよくあります。
findでcd先のディレクトリ絞り込む
どうするか?find
コマンドやfd
コマンド使えば、正規表現使ってマッチしたパスのフルパスを表示してくれます。
$ find . -name "*python*" -type d
-type
オプションを指定すると検索をディレクトリのみに絞ってくれます。
しかし、検索語によっては結果が100件も200件も出てくるかもしれません。
fzfで結果を絞り込む
fzf使えばfindで得られた数百件もの結果をインタラクティブに絞り込めます。絞り込んだあとReturnキー打てば、そのディレクトリのフルパスがSTDOUTに出力されます。
$ find . -name "*" -type d | fzf
最後にその絞り込んだ結果にcd
するだけ。
$ cd $(find . -name "*" -type d | fzf)
上記コマンドは算術演算のカッコのように、先に$()内のコマンドを評価して、その結果をcd
に渡しています。
shellscriptにまとめる
正直こんな長ったらしいコマンドをcd
する度に打ってたら作業効率化どころかむしろ作業時間増えます。
そこで、shellscriptにまとめてdotfilesにでもaliasやfunctionとして書いておいて、ターミナル作業中は短いコマンドで同じ動作が実現できるようにします。
# fd - cd to selected directory
fd() {
local dir
dir=$(find ${1:-.} -path '*/\.*' -prune \
-o -type d -print 2> /dev/null | fzf +m) &&
cd "$dir"
}
これを.bashrcにでも記述してfd
と打てば、カレントディレクトリ以下を検索してすべてのパスからfuzzy-findで絞り込みができます。
fd
はすでにコマンドとして普及している1ので、この関数名はよくないと思います。次に説明するリポジトリが元々こういう関数名だったので残してあります。オプションの項で関数名変えます。
# fda - including hidden directories
fda() {
local dir
dir=$(find ${1:-.} -type d 2> /dev/null | fzf +m) && cd "$dir"
}
隠しディレクトリ.config
のようなディレクトリ以下を探したいときはfda
関数を使います。
fdでも同様に、引数にパスを入れればその下を検索してくれますし、コマンドのみであればカレントディレクトリ下を検索します。
オプション追加
せっかくなのでいろんなcd
を一つのshellscriptにまとめました。それがatweiden/fzf-extrasのfzf-extra.shです。正確には、この中の一部をコミットしました。
zd() {
usage() {
echo "usage: zd [OPTIONS]"
echo "\tzd: cd to selected options below"
echo "OPTIONS:"
echo "\t-d [path]: Directory (default)"
echo "\t-a [path]: Directory included hidden"
echo "\t-r [path]: Parent directory"
echo "\t-s [query]: Directory from stack"
echo "\t-f [query]: Directory of the selected file"
echo "\t-z [query]: Frecency directory"
echo "\t-h: Print this usage"
}
# No arg
if [ ! $1 ]; then
_fd
# Args is '..' or '-' or [path]
elif [ $1 = '..' ]; then
shift; _fdr $1
elif [ $1 = '-' ]; then
shift; _fst "$*"
elif [ ${1:0:1} != '-' ]; then # first string is not -
_fd $(realpath $1)
# Args is start from '-'
else
while getopts darfszh OPT; do
case $OPT in
d) shift; _fd $1; return 0;;
a) shift; _fda $1; return 0;;
r) shift; _fdr $1; return 0;;
s) shift; _fst "$*"; return 0;;
f) shift; _cdf "$*"; return 0;;
z) shift; _zz "$*"; return 0;;
h) usage; return 0;;
*) usage; return 1;;
esac
done
fi
}
例えば上で説明したfd
はzd
で呼び出せます。隠しディレクトリも含めて探すならzd -a
のように-a
オプションをつけます。過去に行ったことのあるディレクトリに飛びたいならzd -z
のように-z
オプションをつけます。
一覧を以下に示します。
zd オプション | 呼出関数 | シンタックスシュガー | 機能 |
---|---|---|---|
zd -d | _fd | zd | カレントディレクトリを再帰的に検索 |
zd -a | _fda | なし | カレントディレクトリを隠しディレクトリ含めて再帰的に検索 |
zd -s | _fst | zd - | ディレクトリスタック2を検索 |
zd -r | _fdr | zd .. | 親ディレクトリを再帰的に検索 |
zd -f | _cdf | なし | ファイルを検索してそのファイルがあるディレクトリにcdする |
zd -z | _zz | zz | 行ったことのあるディレクトリに移動。fasd -dをfzfで絞り込む |
zd -h | usage | なし | helpメッセージを表示 |
fzfを使ったshellscriptが集まるこちらのリポatweiden/fzf-extrasのfzf-extras.shにプルリク送りました。
…がコミットの仕方がまずかったのか、うまいこといかなかったみたいで僕の名前はコミッターに出てきません。残念
動作
動作は以下のgifで確認してください。
zd
Dropbox下のpythonディレクトリを探していますが、間にProgramディレクトリが有ります。
cdならProgramディレクトリを思い出さないとpythonディレクトリにたどり着けませんが、zdならProgramディレクトリを思い出せなくてもpythonディレクトリを見つけることができます。
カレントディレクトリに限らず、引数にディレクトリを指定すればその下のみ検索してくれます。
zd -a
zdでDropbox下の.condaディレクトリを探そうとしましたが、隠しディレクトリのため表示されません。隠しディレクトリに移動するにはzd -a
のように-a
オプションを使います。
オプション-a
はallの頭文字です。
zd -s (zd -)
zd -s
またはzd -
で現在セッションで過去に行ったことのあるディレクトリにcd
できます。
cd -
で一つ前のディレクトリに戻る機能と似ているのでzd -
というシュガーシンタックスを用意しています。
zd -r (zd ..)
/home/u1and0/Dropbox/Program/Dockerfiles/composeset
というディレクトリから上の階層をfuzzy-finderで検索して、何段飛ばしでも上にいけます。
C-jとC-kで上下移動できます。
cd ..
で親ディレクトリに飛ぶことから、zd ..
というシュガーシンタックスを用意しています。
zd -f
zd -f
でカレントディレクトリ下にあるファイルを絞り込み検索して、Returnしたファイルのあるディレクトリにcd
します。
gifではcsvファイルの置いてあるディレクトリ一覧を表示して適当なパスを選んでcd
しています。適当なサンプルファイルを探すときに重宝しています。
オプション-f
はfileの頭文字です。
zd -z (zz)
zd -z
またはzz
使うときはclvv/fasdが必要です。z
コマンドで表示されたディレクトリをインクリメンタルに使いたいことがそもそものモチベーションでした。
fasd -d
(またはd
)でスコアリングされたfrecencyディレクトリ(頻繁にfrequency + 最近recently)を表示しています。
これをzd -z
またはzz
でfuzzy-findします。
fasdをインストールすると、これまで行ったディレクトリ(と開いたファイル)すべてが~/.fasdに保存されますので、zd -s
と異なり、現在セッションだけでなく、これまでアクセスしたことのあるすべてのディレクトリを検索対象にできます。
-
現在セッションで過去に居たディレクトリ ↩