zshにおけるディレクトリ操作について ~ 基礎 & 脱コピペ ~


はじめに

100%趣味で書かれた記事です。

脱コピペを目標にmanを読みつつzshの理解を深めようのコーナー。
ほぼmanの翻訳みたいになったけど、ディレクトリ操作についてコメント混じりで書いていきます。

書くこと

  • オプション、各種コマンドについて
  • 筆者の設定(zshrc)について

書いてないこと

  • zshの導入について
  • プラグインについて
  • 名前付きディレクトリについて

OS&version

Version
OS macOS Catalina
zsh 5.8.0

cd関連のオプション一覧

  • AUTO_CD
    ディレクトリ名だけを入力した場合に、そこにcdする
    cd ぐらい入力したいけど ..で親に戻るときの便利さ故に手放せない

  • AUTO_PUSHD
    ディレクトリ移動時に、移動元をディレクトリスタックにpushする

  • CDABLE_VARS
    指定したディレクトリが存在しない場合、名前付きディレクトリ展開を試みる
    補完時に 意図してない ディレクトリが表示されるので 設定には注意

  • CD_SILENT
    cdした後に、カレントディレクトリを表示しない。
    ※ デフォルト(OFF)時には cd の引数が - だったり、ディレクトリスタック指定、cdpath内のパスを指定したとき 移動後に カレントディレクトリを表示する仕様になってるよ

  • CHASE_DOTS
    パスに .. が含まれる場合、物理パスの親ディレクトリとして変換する

  • CHASE_LINKS
    パス名に含まれるすべてのシンボリックリンクを物理パスに変換する

  • PUSHD_IGNORE_DUPS
    ディレクトリスタックに同じ名前のものをpushしない(重複除去)

  • PUSHD_MINUS
    スタックへのアクセス時に、'+''-'の意味を反転させる

  • PUSHD_SILENT
    push,またはpopdの後にディレクトリスタックを出力しない

  • PUSHD_TO_HOME
    引数無しでpushdすると pushd $HOME となる

cd関連の変数一覧

  • cdpath
    cdコマンドの検索パスを指定する。 ディレクトリを列挙する配列

  • DIRSTACKSIZE
    ディレクトリスタックの最大サイズ

cd

zshのbuiltin-command cd について
特徴的なとこ:第2引数がとれるくらい。

書式

cd [ -qsLP ] [arg1 [arg2] | {+|-}N]

arg1 を省略したら$HOMEに移動する。
arg1 が「-」だったら移動前のディレクトリに移動する。

それ以外はこんな感じ

  1. arg1が /で始めるときは普通に移動
  2. カレントディレクトリ以下から argを探す
  3. cdpathからargを探す
  4. 名前付きディレクトリとしてargを探す
  5. 上記のいずれにも当てはまらない場合は失敗
オプション一覧
option 説明
-q chpwd関数に登録されたhookを実行しない
-s パス名にシンボリックリンクが含まれる場合は移動しない
-P シンボリックリンクを物理パスに直して移動
-L シンボリックリンクを物理パスに直さない。

pushd, dirs

移動元のディレクトリをスタック状に保存できる。 この記憶領域をディレクトリスタックと呼ぶ
ここではあんまり詳細には説明しない。
AUTO_PUSHD オプションがあるので、pushd単体で使うことそうそうない気がする。
pushdで ディレクトリスタックにpush。dirs でディレクトリスタックの中身が確認できる

pushd
pushd [ -qsLP ] [arg1 [arg2] | {+|-}N]

オプションは cd と同じ

dirs
dirs [ -lpvc ]
  • -l :ディレクトリスタックを全てフルパスで表示
  • -p:一行づつ表示
  • -v:番号つきで表示。

cd でディレクトリスタック参照

cd -{+|-}Nでディレクトリスタックを参照して 移動できる。
+の場合は、左(新しいほう)から、 -の場合は 右(古い方)から数える
cd -<ここで補完が聞いて便利ですね>

# pwd → /tmp/x
% dirs
/tmp/x /tmp/x/d /tmp/x/c /tmp/x/b /tmp/x/a

# 補完
% cd +1
1 -- /tmp/x/d
2 -- /tmp/x/c
3 -- /tmp/x/b
4 -- /tmp/x/a

# 補完2
% cd -0
0 -- /tmp/x/a
1 -- /tmp/x/b
2 -- /tmp/x/c
3 -- /tmp/x/d

前者のほうが直感的だと思います。
かつ cd - との兼ね合いで - のほうがわかりみが深いので PUSHD_MINUS オプションを指定してもいいかもしれません。

Remembering Recent Directories

真打ち登場。cdrコマンド
移動したディレクトリを履歴化、参照して移動。ということが可能になる
ディレクトリスタック(pushd, dirs) と似てるけど cdrではディレクトリスタックを使用しない
以下 chpwd_recent_dirs で積まれるディレクトリ履歴一覧を ディレクトリリストと呼ぶことにする
デフォルトで重複除去してくれるからいいですね

導入
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook 
add-zsh-hook chpwd chpwd_recent_dirs
書式
cdr [-l | -r | -e | -p <pattern> | <dir-num> ]

<dir-num>はディレクトリリストの番号。 1は直前のディレクトリとなる
引数を省略した場合は cdr 1 と同義
ディレクトリリストおよびリストの順序は別ウィンドウと共通なので注意
後述する recent-dirs-default を設定しているとき、 数値以外の引数の場合 cd と同じ動作になる

オプション一覧
オプション 説明
-l ディレクトリリストの表示。(~置換した状態で表示される)
-r 変数replyをディレクトリセットに設定する。
-e ディレクトリリストを編集する。 zleエディタぽい。
-p パターンにマッチする項目をディレクトリリストから削除する
実行後、確認を求められる。 省略したい場合は -P

設定

zstyle ':chpwd:*' <ここで指定するよ>

recent-dirs-default

trueにすると、 コマンド引数に数値(index)以外、 または引数が複数指定された場合は cd コマンドとして扱う

recent-dirs-file

ディレクトリリストが保存されるファイル
デフォルトは ${ZDOTDIR:-$HOME}/.chpwd-recent-dirs
深いこと考えなければデフォルト(設定無し)安定

recent-dirs-insert

補完候補に何を表示するかの設定。

recent-dirs-default がtrue かつ この項目が trueのとき、インデックスではなくディレクトリ名が補完される。
履歴に cdr <dir-num> の形式で残らないのでいいかも

以下、補完候補として表示されるリストとして
ディレクトリリストをA
カレントディレクトリ配下の移動先一覧を B とする

  • always : 常にAのみ補完候補に表示する。
  • fallback:Aを補完候補に表示する。Aが空の場合はBを補完する
  • both:Aの後続にBを補完候補として表示する

always でいい気もするが、 cdr=cd として使ってる人は fallback both がお勧めです。

recent-dirs-max

ファイルに保存するディレクトリの最大数。 デフォルトは 20

recent-dirs-prune

履歴として積まない ディレクトリを指定する

  • parent
    ディレクトリリストから 親(祖先)を追加しない。
    ホームディレクトリから ~/some~/some/dirs と移動したとする
    この時 ~~/some は リストに追加されない。

  • pattern:<patten>
    追加しないパターンを指定できる。

recent-dirs-pushd

trueに設定すると、 cdrで pushdを使用するため ディレクトリスタックが積まれる。
ドキュメントにもあるが、 cdr ではディレクトリリストを使用するので こいつを設定するのはあんまり意味がない。

まとめ

まとめというか、ディレクトリ操作に関する.zshrcの記述

# -------------------
# changing directories
# -------------------

cdpath=(~)

setopt auto_cd
setopt auto_pushd
setopt pushd_minus
setopt pushd_ignore_dups

autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd chpwd_recent_dirs

zstyle ':chpwd:*' recent-dirs-default true
zstyle ':chpwd:*' recent-dirs-max 500
zstyle ':chpwd:*' recent-dirs-file \
  $HOME/.cache/shell/.chpwd-recent-dirs-${TTY##*/} +
zstyle ':chpwd:*' recent-dirs-prune 'parent'
zstyle ':completion:*' recent-dirs-insert always

補足

chpwd_recent_dirs に合わせてディレクトリスタックも使用する設定にしている。

・過去移動したことあるディレクトリへの移動 →cdr
・最近移動したディレクトリへの移動 → cd -でディレクトリ補完

という感じで用途を切り分けてます。前者に関しては筆者は + fzfで絞り込みしてます。

zstyle ':chpwd:*' recent-dirs-file \
  $HOME/.cache/shell/.chpwd-recent-dirs-${TTY##*/} +

この部分はmanにも記載のある設定です。端末毎にディレクトリリストを分けることが可能です。

参考

zsh: The Z Shell Manual