16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

fuzzy-finderでディレクトリ探しながらcd

Last updated at Posted at 2019-12-11

ターミナル操作の8割はcdとlsコマンドで操作するとかいう記事をどっかで見かけたような見かけないような。
ディレクトリ移動するだけなのに、パス名を思い出すだけなのにそんなに作業時間かけたくないですよね?

bashではcdtabでカレントディレクトリ一覧が表示されたり、パスの途中まで打ち込んでいれば残りを補完してくれます。
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
}

例えば上で説明したfdzdで呼び出せます。隠しディレクトリも含めて探すなら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-extrasfzf-extras.shにプルリク送りました。
…がコミットの仕方がまずかったのか、うまいこといかなかったみたいで僕の名前はコミッターに出てきません。残念

Screenshot from 2019-12-11 21-41-04.png

動作

動作は以下のgifで確認してください。

zd

Peek 2019-12-11 21-48.gif

Dropbox下のpythonディレクトリを探していますが、間にProgramディレクトリが有ります。
cdならProgramディレクトリを思い出さないとpythonディレクトリにたどり着けませんが、zdならProgramディレクトリを思い出せなくてもpythonディレクトリを見つけることができます。

Peek 2019-12-11 23-09.gif

カレントディレクトリに限らず、引数にディレクトリを指定すればその下のみ検索してくれます。

zd -a

Peek 2019-12-11 22-02.gif

zdでDropbox下の.condaディレクトリを探そうとしましたが、隠しディレクトリのため表示されません。隠しディレクトリに移動するにはzd -aのように-aオプションを使います。
オプション-aはallの頭文字です。

zd -s (zd -)

Peek 2019-12-11 22-11.gif

zd -sまたはzd -で現在セッションで過去に行ったことのあるディレクトリにcdできます。
cd -で一つ前のディレクトリに戻る機能と似ているのでzd -というシュガーシンタックスを用意しています。

zd -r (zd ..)

Peek 2019-12-11 22-17.gif

/home/u1and0/Dropbox/Program/Dockerfiles/composesetというディレクトリから上の階層をfuzzy-finderで検索して、何段飛ばしでも上にいけます。
C-jC-kで上下移動できます。

cd ..で親ディレクトリに飛ぶことから、zd ..というシュガーシンタックスを用意しています。

zd -f

Peek 2019-12-11 22-23.gif

zd -fでカレントディレクトリ下にあるファイルを絞り込み検索して、Returnしたファイルのあるディレクトリにcdします。
gifではcsvファイルの置いてあるディレクトリ一覧を表示して適当なパスを選んでcdしています。適当なサンプルファイルを探すときに重宝しています。
オプション-fはfileの頭文字です。

zd -z (zz)

zd -zまたはzz使うときはclvv/fasdが必要です。zコマンドで表示されたディレクトリをインクリメンタルに使いたいことがそもそものモチベーションでした。

Peek 2019-12-11 22-55.gif

fasd -d(またはd)でスコアリングされたfrecencyディレクトリ(頻繁にfrequency + 最近recently)を表示しています。
これをzd -zまたはzzでfuzzy-findします。
fasdをインストールすると、これまで行ったディレクトリ(と開いたファイル)すべてが~/.fasdに保存されますので、zd -sと異なり、現在セッションだけでなく、これまでアクセスしたことのあるすべてのディレクトリを検索対象にできます。

  1. sharkdp/fd

  2. 現在セッションで過去に居たディレクトリ

16
16
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?