Magnolia Tech

いつもコードのことばかり考えている人のために。

zshの設定ファイルが読み込まれるタイミング

zshの設定を見直すために、改めてzshの設定ファイルの意味や、読み込まれるタイミングなどを調べたら、けっこう環境によって違うので、まとめておく

ドキュメントでは設定ファイルがどのフェーズで読み込まれるかは解説してくれるが、それぞれの設定ファイルに「何を書くべきか?」「何を書くべきではないか?」は教えてくれない

どのようなタイミングで読み込まれるかを知っておくことで、それを決めることができるようになるはず

設定ファイルの種類と、読み込まれるタイミング

.zshenv

全ての環境で必ず読み込まれる

そのため、あまり設定を入れ過ぎない方がよい

自分の環境では後述する.zprofileと、.zshrcの読み込み先を$HOME/.config/zshディレクトリとする設定だけを入れている

export ZDOTDIR=$HOME/.config/zsh

こうすることで.configディレクトリ配下をgitのリポジトリとしてバージョン管理できるようになる

.zprofile

ログインシェルで読み込まれる

echo $0の出力が-zsh(コマンド名の先頭がハイフン)の場合は、ログインシェル

「ログイン」と付いているが、必ずどんな環境でもユーザーがログインした時に一度だけ実行されるという意味ではない

後述するが、環境によってまったく読み込まれなかったり、ユーザーログイン時に一度だけ読み込まれたり、ターミナルのウインドウが立ち上がる度に何度も読み込まれたりする

読み込まれるタイミングや回数に最も気をつけるべき設定ファイル

ログインシェル、かつ非インタラクティブシェル、という環境もあるので、出力結果が表示されるとは限らない

つまり、設定する内容はPATHの設定など、環境変数の設定のみに絞った方がよい

.zshrc

インタラクティブシェルで読み込まれる

echo $-の出力にiが含まれている場合は、インタラクティブシェル

履歴や、プロンプトの設定、gitのリポジトリ状態の表示など、シェルにの操作、入出力に関する設定を書く

OSや環境による違い

macOS

ターミナルエミュレータのウインドウを開く度に「ログインシェルかつ、インタラクティブシェル」が立ち上がる

.zprofileはユーザーログイン以降、複数回読み込まれることを前提に書くべきと言える

ただし、設定で非ログインシェルに変えられるため、ターミナルエミュレータの設定との整合性には要注意

他のプロセスに影響するような、例えば「設定ファイルへ追記」のような変更はすべきではない

現在のプロセスと、その子プロセスのみに影響する「環境変数」の設定に絞った方がよい

なお、macOSのターミナルエミュレータは、Terminal.appだけでなく、macOS専用のiTerm2や、クロスプラットフォームGhosttyも全てデフォルトではログインシェルを起動する

Linux

Ubuntu 24.4で確認

ログイン方法によって異なるため、要注意

  • CUIコンソールへログインすると、「ログインシェルかつ、インタラクティブシェル」が立ち上がる

  • sshでログインすると、「ログインシェルかつ、インタラクティブシェル」が立ち上がる

    ただし、コマンド起動を直接指定している場合は、シェルを経由しないため、必要な環境設定が行われない可能性に注意

  • X WindowベースのGNOMEにログインした後、ターミナルエミュレータを立ち上げると、「非ログインシェル、かつインタラクティブシェル」が立ち上がる

    デフォルトではログインシェルではないため、明示的に読み込むように設定しないと.zprofileは読み込まれない

    ターミナルエミュレータの設定を変えれば、立ち上げた時に「ログインシェル」となるように変えられ、その時は.zprofileが読み込まれる(macOSの逆)

  • WaylandベースのGNOMEにログイン時に、「ログインシェル、かつ非インタラクティブシェル」が実行され、そこからウインドウマネージャーが立ち上がる

    つまり、この時点で.zprofileが読み込まれ、環境変数が設定され、以降のプロセスにはこの環境変数が引き継がれる

    ターミナルエミュレータを立ち上げると、「非ログインシェル、かつインタラクティブシェル」が立ち上がるが、ログイン時に.zprofileが読み込まれているため、環境変数は引き継がれている

    ターミナルエミュレータの設定を変えれば、ログインシェルにできるが、既に.zprofileは読み込まれた状態なので、わざわざ変える必要は無いと思われる


WaylandベースのGNOMEにログインした時に、ログインシェルが実行される仕組みは、以下のソースコードを参照

github.com

ここでは、興味深いテクニックが使われてる

  1. 普通にシェルスクリプトとして起動する

  2. ユーザーのシェルを取得

  3. そのシェルをログインシェルとして実行する、その際実行中のプロセスと置き換える形で起動する

  4. 新たにログインシェルとして起動したプロセスの中で、再度スクリプトを実行

  5. ログインシェルとして立ち上がっていると、上記のロジックがスキップされる

  6. GNOMEのセッションマネージャーが起動し、ウインドウの起動に移る

  7. ログインシェルとして実行されているので、.zprofileが読み込まれているので、その中で設定された環境変数は以降のプロセスに引き継がれていく...

ハック!って感じだ


X Windowと、Waylandの設計思想の違いが見てとれて面白いけど、Waylandベースの方がしっくりくる


上記の調査結果を踏まえると...

  • .zshenvには極力なにも書かない
  • .zprofileには環境変数の設定(PATHの追加など)に絞る
  • .zshrcにはシェルの操作や、入出力に関するもの、.zshenvや、.zprofileに書けないものを書く
  • X Windowベースの環境では、ログイン時に.zprofileが読み込まれるように設定の追加を行う

    (SSHでのリモートログイン時にはログインシェルとなるので、一貫性を確保するため)

という方針でzshの設定ファイルを書くのが良さそう