どうにも忘れがちなので、bash の設定ファイルの実行(ロード)処理を改めて整理してみた。ちょっとやる気を出して、今回は図付き。また各種設定ファイルの使い分けについても最後に書いておく。
- はじめに
- 設定ファイルの実行(ロード)順序
- 各ディストリビューションの実装状況
(2019年12月29日:macOS の場合を追記、および章名等を含め表現改善)
はじめに
Bash は起動方法によって異なる設定ファイルを実行する。どの設定ファイルを実行するかを左右するのは、「ログイン用 (login) かどうか」、および「対話的インタフェースを提供する (interactive) かどうか」。合計 4 パターンある、これらの組み合わせのうち、「ログイン用かつ対話的インタフェース無し」は現実的に使われないので、以下 3 パターンだけを意識すれば良い:
以下、それぞれの場合について設定ファイル実行に関する動作を説明していく。ただし、簡単のため以下の用語を定義しておく:
*profile
系/etc/profile
,~/.bash_profile
,~/.profile
などを指す
*rc
系/etc/bash.bashrc
,/etc/bashrc
,~/.bashrc
などを指す
設定ファイルの実行順序
1. Interactive login shell の場合
対話ログインシェル (interactive login shell) は Linux へのログイン時に自動的に起動するシェルが該当する。具体的な例としては、コンソールログイン(非 GUI ログイン)した場合や、ssh でリモートからログインした場合が挙げられる。
この場合、次のように各種設定ファイルは実行される:
/etc/profile
が存在すれば、それを実行- 続いて:
~/.bash_profile
が存在すれば、それを実行。さもなくば:~/.bash_login
が存在すれば、それを実行。さもなくば:~/.profile
が存在すれば、それを実行
- ログインセッションを実施
- ログアウト時に:
~/.bash_logout
が存在すれば、それを実行
この動作を、次に図示する:
~/.bash_profile
と ~/.bash_login
、 ~/.profile
の 3 ファイルについては「いずれか一つだけ」を実行する点に注意が必要(優先順位は、上記箇条書きの記載順)。
なお Gnome Terminal 等の GUI の端末エミュレータで起動した bash は一般的に interactive shell に該当する。というのも、実のところ GUI システム自体がログインシェルから起動されているので、その延長で起動する bash はログインシェルではない、ということ。ただし、bash には「ログインシェルとして起動したかのように振る舞う」コマンドラインオプション -l/--login
があり、CUI ウィンドウを開くと同時にこのオプションを使って bash を起動する端末エミュレータもある(macOS 標準の「ターミナル」アプリなど)。技術的には interactive shell であっても設定ファイルは interactive login shell の仕様で実行される環境もある、ということなので、GUI 端末エミュレータを使っている場合は bash の起動オプションも確認しておいた方が良いと思う。
2. Interactive (non-login) shell の場合
ログインシェルではない対話シェル (interactive shell) は、ログインの後に改めて起動された bash が該当する。具体的な例としては、(1) Gnome Terminal のような GUI の端末エミュレータアプリを起動したとき(そのアプリの中で動いている bash)や、(2) su
や sudo bash
等のコマンドで root 昇格したとき(su -
や sudo -i
はログインシェル同様の動作)、(3) Pipenv のようなツールでシェルを起動したとき、などが挙げられる。
この場合、次のように各種設定ファイルが実行される:
/etc/bash.bashrc
が存在すれば、それを実行~/.bashrc
が存在すれば、それを実行
この動作を、次に図示する:
つまり *rc
系ファイルだけを実行する。そして *profile
系ファイルは実行されない。また /etc/bashrc
という「それっぽい」ファイルを提供している環境もあるけれど、bash 自体にとっては特別なファイルでも何でもない。
3. Non-interactive shell の場合
ログインシェルでも対話シェルでもない形で bash が起動するのは、おそらくシェルスクリプト実行時ぐらいかと思うので、以下はシェルスクリプト実行時を前提に書いていく。この場合、次のように各種設定ファイルが実行される:
- 環境変数
BASH_ENV
が定義されており、その値が存在するファイルであれば、それを実行
この動作を、次に図示する:
cron による自動実行ジョブで特別に通常と異なる環境でシェルスクリプトを実行する、といった使い方をするのだろうか。まあ、この環境変数を意識せずに使うことは考えにくいので、一般的にはシェルスクリプト実行時に bash は何も設定ファイルを実行しないと捉えて良いのだろうと考えている。
なお bash は実行ファイルの名前が sh
だった場合にオリジナル Bourne Shellとの互換性を重視した特別なモードで動作する。そのため、明示的に bash でシェルスクリプトを実行しない限り(例えば #/bin/bash
というシェバンを使ったり、bash foo.sh
などと実行しない限り)、この場合には該当しない。その場合の動作はというと、上記の環境変数名を ENV
に置き換えた仕様となる。
各ディストリビューションの実装状況
ざっと思いついた Linux ディストリビューションと macOS で状況を調べたところ、すべて何らかの形で *profile
系設定ファイルで *rc
系設定ファイルを source していたものの、細かい部分で違っていた:
- Debian 9, 10, Ubuntu 16.04, 18.04
/etc/profile
で/etc/bash.bashrc
を source~/.profile
で~/.bashrc
を source
- CentOS 8
/etc/profile
で/etc/bashrc
を source~/.bash_profile
で~/.bashrc
を source~/.bashrc
で/etc/bashrc
を source
- CentOS 6, 7
~/.bash_profile
で~/.bashrc
を source~/.bashrc
で/etc/bashrc
を source
- Arch Linux 2019-11-10
/etc/profile
で/etc/bash.bashrc
を source~/.bash_profile
で~/.bashrc
を source
- openSUSE 15.2
/etc/profile
で/etc/bash.bashrc
と~/.bashrc
を source
- macOS 10.15.2 "Catalina"
/etc/profile
で/etc/bashrc
を source- ホームディレクトリに bash 関連の設定ファイルが無い
ディストリビューションごとに流儀が違っていて、面白いね😇
*profile
系と *rc
系ファイルの使い分け
ここまで複雑だと
前節で書いたように *profile
系と *rc
系で設定ファイルを二段構えにすると、価値のある使い分けが可能になる。具体的には、*profile
系ファイルはログインセッションの最初に一回だけ、*rc
系ファイルはシェルが起動するたびに実行されるので、次のように使い分けをしておくと良い。
*profile
系に書くべき内容
- ログイン直後に一回だけ実行したい処理
uptime
でサーバーの連続稼働時間を表示する等
- 対話操作には不要だけれどシェルスクリプトの実行には必要な設定
- 環境変数
LANG
やJAVA_HOME
等を定義
- 環境変数
- その他ほとんど変わることの無い設定
- 環境変数
PATH
やINCLUDE
等を定義
- 環境変数
*rc
系ファイルに書くべき内容
- 暗黙的にサブシェルに引き継がれない定義
- エイリアスの定義
- 関数の定義
- 非対話ログインで不要かつ対話ログインで使いたい設定
- 環境変数
PS1
やEDITOR
,HISTIGNORE
などの定義
- 環境変数
なお rc
系ファイルでメッセージ表示するような処理を含める場合、標準出力が端末に接続されている場合にのみメッセージ出力した方が良い。さもなければ scp
が正常動作しない等の問題が起こってしまう。標準出力が端末に接続されているかどうかは、たとえば if test -t 0; then ...; fi
あるいは if tty > /dev/null; then ...; fi
などとすればプログラム的に確認できるのだけれど、まあ最初からそういう処理を含めないのが一番良いのだろうね。