bashの.profileや.bashrc等を実行する動作仕様

どうにも忘れがちなので、bash の設定ファイルの実行(ロード)処理を改めて整理してみた。ちょっとやる気を出して、今回は図付き。また各種設定ファイルの使い分けについても最後に書いておく。

はじめに

Bash は起動方法によって異なる設定ファイルを実行する。おそらく次の 3 種類の場合を把握しておけば混乱することは無いと思う。

  1. Interactive login shell の場合
    • ログインと同時に起動する bash はコレ
  2. Interactive (non-login) shell の場合
    • ログインシェル上で bash を起動した場合など
  3. Non-interactive shell の場合
    • シェルスクリプトを実行する場合など

なお "interactive" は「対話的」ということで、ユーザーがコマンドをキーボードで入力したり、その出力を確認したりできることを指している。また "login shell" は「ログイン直後に自動起動するシェル」を指している。

以下、それぞれの場合について説明を書いていく。ただし、簡単のため以下の用語を定義しておく:

  • *profile
    • /etc/profile, ~/.bash_profile, ~/.profile などを指す
  • *rc
    • /etc/bash.bashrc, /etc/bashrc, ~/.bashrc などを指す

設定ファイルの実行動作

1. Interactive login shell の場合

f:id:sgryjp:20191109181139p:plain
Interactive login shell である bash の設定実行処理

対話ログインシェル (interactive login shell) は Linux へのログイン時に自動的に起動するシェルが該当する。具体的な例としては、コンソールログイン(非 GUI ログイン)した場合や、ssh でリモートからログインした場合が挙げられる。この場合、次の動作となる:

  • /etc/profile が存在すれば、それを実行
  • 続いて:
    • ~/.bash_profile が存在すれば、それを実行
    • ~/.bash_login が存在すれば、それを実行
    • ~/.profile が存在すれば、それを実行
  • ログインセッション
  • ログアウト時:
    • ~/.bash_logout が存在すれば、それを実行

基本的には *profile 系ファイルを実行するのだけれど、~/.bash_profile~/.bash_login~/.profile の 3 ファイルについては「いずれか一つだけ」を実行する点に注意が必要(優先順位は、上記箇条書きの記載順)。この挙動は他の zsh などとは違っており、分かりにくい。

なお Linux を GUI (X Window) 環境で使っている場合、CUI 操作を行うために端末エミュレータ (Gnome Terminal 等) を起動すると思う。このとき、実際には GUI 自体がログインシェル(上記箇条書きの 1 番目に該当)から起動されているため、その延長で起動する bash は一般的にログインシェルではなく1次項(上記箇条書きの 2 番目)に該当する。

2. Interactive (non-login) shell の場合

f:id:sgryjp:20191109181156p:plain
Interactive shell である bash の設定実行処理

ログインシェルではない対話シェルは、ログイン後に改めて bash を起動した場合が該当する。具体的な例としては、(1) XTerm や Gnome Terminal といった GUI の端末エミュレータを起動したとき(そのアプリの中で動いている bash)や、(2) susudo bash 等のコマンドで root 昇格したとき(su -sudo -i はログインシェル同様の動作)、(3) Pipenv のようなツールでシェルを起動したとき、などが挙げられる。この場合、次の動作となる:

  • /etc/bash.bashrc が存在すれば、それを実行
  • ~/.bashrc が存在すれば、それを実行

つまり *rc 系ファイルだけを実行する。単純だ。

3. Non-interactive shell の場合

f:id:sgryjp:20191109190153p:plain
Non-interactive shell である bash の設定実行処理

ログインシェルでも対話シェルでもない形で bash が起動するのは、おそらくシェルスクリプト実行時ぐらいかと思う。この場合、次の動作となる:

  • 環境変数 BASH_ENV が定義されており、その値が存在するファイルであれば、それを実行

なお bash は実行ファイルの名前が sh だった場合にオリジナル Bourne Shellとの互換性を重視した特別なモードで動作する。そのため、明示的に bash でシェルスクリプトを実行しない限り(例えば #/bin/bash というシェバンを使ったり、bash foo.sh などと実行しない限り)、この場合には該当しない。

注意:たいてい.(bash_)profileから.bashrcが実行される

bash の仕様では *profile 系と *rc 系のどちらを実行するかが起動方法によって切り替わる。が、丸ごと切り替えられても嬉しくないと多くの Linux ユーザーが思っているらしく、多くの Linux ディストリビューションでは対となる rc 系ファイルを読み込む("source" する)命令が書かれた profile 系ファイルを標準提供している。ざっと思いついた Linux ディストリビューションで状況を調べたところ、以下のようになっていた:

  • Debian 9, 10, Ubuntu 16.04, 18.04, Arch Linux 2019-11-10
    • /etc/profile/etc/bash.bashrc を source
    • ~/.profile~/.bashrc を source (Arch では ~/.profile の代わりに ~/.bash_profile
  • 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
  • openSUSE 15.2
    • /etc/profile/etc/bash.bashrc~/.bashrc を source

ディストリビューションごとに流儀が違っていて、面白いね。

補足: *profile 系と *rc 系ファイルの使い分け

前節で書いたように *profile 系と *rc 系で設定ファイルを二段構えにすると、価値のある使い分けが可能になる。具体的には、*profile 系ファイルはログインセッションの最初に一回だけ、*rc 系ファイルはシェルが起動するたびに実行されるので、次のように使い分けをしておくと良い。

*profile 系に書くべき内容

  1. ログイン直後に一回だけ実行したい処理
    • uptime でサーバーの連続稼働時間を表示する等
  2. ほとんど変わることの無い設定
    • 環境変数 LANGPATH, INCLUDE, JAVA_HOME 等を定義
  3. 対話操作には不要だけれどシェルスクリプトの実行には必要な設定
    • (あまり良い例が思いつかない。。)

rc 系ファイルに書くべき内容

  1. 暗黙的にサブシェルに引き継がれない定義
    • エイリアスの定義
    • 関数の定義
  2. 非対話ログインで不要かつ対話ログインで使いたい設定
    • 環境変数 PS1EDITOR, HISTIGNORE などの定義

なお rc 系ファイルでメッセージ表示するような処理を含める場合、標準出力が端末に接続されている場合にのみメッセージ出力した方が良い。さもなければ scp が正常動作しない、あるいは clush で得られる結果にそのメッセージが混入する等の問題が起こってしまう。標準出力が端末に接続されているかどうかは、たとえば test コマンドを使って if test -t 0; then ...; fi (if [ -t 0 ]; then ...; fi) としたり、tty コマンドを使って if tty > /dev/null; then ...; fi としたりすればプログラム的に確認できるのだけれど、まあ、最初からそういう処理を含めないのが一番良さそうだなと感じてはいる。


  1. ログインシェルでなくとも、bash のオプション -l/--login を使えばログインシェルと同様に *profile 系ファイルもロードするよう指示できる。