Lambdaカクテル

ソフトウェア開発者です.玉石混淆です.

.bash_profileや.bashrcたちの読み込み順序を補習する

勉強のメモです.

tl;dr

  • bashプロセスをまたいで有効な処理は~/.bash_profileに書け.そうでない処理は~/.bashrcに書け.
    • 環境変数はプロセスをまたいで有効.
    • エイリアスやシェル拡張系プログラム(fzfなど)の設定はプロセスをまたげない.
  • いかなる場合も,bashはファイルの実行に1つでも失敗するとエラーを報告する.
    • ファイルがない場合はこれに含まない.
  • ログインすると,/etc/profileがあれば読み込まれ,~/.bash_profile,(~/.bashrcに指定されていれば~/.bashrc),~/.bash_login~/.profileのうち,この順で最初に存在したものが読み込まれる.
    • 終了時には~/.bash_logoutがあれば実行される.
  • シェルからシェルを対話的に起動すると,~/.bashrcが読み込まれる.
    • rcrun commandsの頭字語である.
  • シェルスクリプトなどで非対話的に起動すると何も読み込まれないが,$BASH_ENVでファイルを指定できる.
  • sshでログインすると,~/.bashrcが読み込まれる.
  • shとして起動すると,shの動作になる.

インタラクティブ・シェル,インタラクティブなログイン・シェル,非インタラクティブ・シェル

インタラクティブ(対話的)・シェルとは,-Xといった形式の引数が無く*1-cオプションが指定されていない状態で,標準入力と標準エラー出力が端末に接続されて起動したbash,もしくは-iオプションをともなって起動したbashのことである.ただし,-sオプションを伴った場合はインタラクティブ・シェルである.

一文では分かりにくいので,以下のように考える.

  1. -iオプションがあるなら,インタラクティブ・シェルである.
  2. -s オプション(標準入力を実行するオプション)があるなら,インタラクティブ・シェルである.
  3. オプションでない引数(典型的にはファイル名)があるなら,インタラクティブ・シェルではない.
  4. -cオプション(指定したコマンドを実行するオプション)があるなら,インタラクティブ・シェルではない.
  5. 入力とエラー出力が両方とも端末に接続されていなければ,インタラクティブ・シェルではない.
  6. 以上にあてはまらなければ,インタラクティブ・シェルである.

ログインシェルは,loginによって起動されたか,--loginオプションをともなって起動したインタラクティブシェルである.インタラクティブシェルはログインシェルとは限らない.インタラクティブシェルとはバッチファイルで起動したり,コマンドを渡されて起動するものである.

インタラクティブなログインシェルとして起動したとき(--loginオプションをともなって起動したとき)

/etc/profileがあれば実行する.~/.bash_profile~/.bash_login~/.profileをその順通りに,最初に見付かったものを実行する. 終了時またはexitが実行されたときには~/.bash_logoutがあれば実行する.

また典型的には~/.bash_profileの末尾には「~/.bashrcがあれば読み込む」という動作が記載されているため,~/.bash_profile->~/.bashrcの順で読み込まれることとなる.

インタラクティブだが非ログインシェルとして起動したとき

~/.bashrcがあれば実行する.ただし,--norcオプションを指定したときは読み込まない.また,--rcfile FILEによって読み込むファイルを指定したときは,~/.bashrc代わってそのファイルがあれば実行される(そのファイルが存在しない場合は読み込まないという動作が踏襲され,エラーにはならない).

インタラクティブシェルとして起動したとき

典型的には,シェルスクリプトとして起動している場合である.

bashはまず$BASH_ENV環境変数から読み取り,その中の値を一般的な文字列について行うように展開し,得られた値が指し示すファイルを実行しようとする.例えば$BASH_ENV$HOME/.hogeであったとき,$HOMEは展開され,ホームディレクトリ中の.hogeが実行され,後に続く(典型的にはシェルスクリプトの)処理が継続される.ただし,ファイルの検索に$PATHは利用されない.

また,--loginを指定することで前掲の動作を行う.

shとして起動したとき

歴史的なshの動作を模倣しようとする.後述するようなファイルの読み込みが終了すると,bashPOSIXモードへと遷移する.

インタラクティブ・ログインシェルまたは--loginをともなって起動したとき

/etc/profile~/.profileをその順で実行しようとする.ただし,--noprofileをともなって起動したときは実行しない.

インタラクティブ・シェルとして起動したとき

bashはまず$ENV環境変数から読み込み,その中の値を(bash-likeに?)展開し,得られた値が指し示すファイルを実行しようとする.

shの動作を模倣しているので,--rcfileオプションは機能しない.

インタラクティブ・シェルとして起動したとき

何も読み込まない.

POSIXモードで起動したとき

--posixオプションを指定するとbashPOSIXモードで起動する.POSIXモードでは,bashはまず$ENV環境変数から読み込み,その中の値を(bash-likeに?)展開し,得られた値が指し示すファイルを実行しようとする.他のファイルは実行されない.

リモートシェルデーモンとして起動したとき

典型的にはrshやsshの接続で起動した場合である.bashはネットワーク接続越しの起動であることを検知すると,『インタラクティブだが非ログインシェルとして起動したとき』と同様に起動する. ただし,rshdやsshd--norc--rcfileをともなってbashを起動することは基本的にしないため,~/.bashrc読み込みの阻害や他ファイル読み込みの強制は結果的にできない.

実際のUID/GIDと異なるUID/GIDで起動したとき

典型的にはsudoで起動したときである. ファイルを読み込まない.環境設定は継承されない.環境変数SHELLOPTSBASHOPTSCDPATHGLOBIGNOREが出現してもこれらを無視する. ただし-pオプションをともなって起動したときは通常と同様に起動するが,実効的なUIDは変化しない.

感想

そんなに難しくなかったので,zshの場合にチャレンジしたい(難しそう).

参考文献

tiswww.case.edu

Man page of BASH

*1:原文では"without non-option arguments"