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

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

  1. はじめに
  2. 設定ファイルの実行(ロード)順序
  3. 各ディストリビューションの実装状況
  4. *profile 系と *rc 系ファイルの使い分け

  5. (2019年12月29日:macOS の場合を追記、および章名等を含め表現改善)

はじめに

Bash は起動方法によって異なる設定ファイルを実行する。どの設定ファイルを実行するかを左右するのは、「ログイン用 (login) かどうか」、および「対話的インタフェースを提供する (interactive) かどうか」。合計 4 パターンある、これらの組み合わせのうち、「ログイン用かつ対話的インタフェース無し」は現実的に使われないので、以下 3 パターンだけを意識すれば良い:

  1. Interactive login shell の場合
  2. Interactive (non-login) shell の場合
  3. Non-interactive shell の場合

以下、それぞれの場合について設定ファイル実行に関する動作を説明していく。ただし、簡単のため以下の用語を定義しておく:

  • *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 が存在すれば、それを実行

この動作を、次に図示する:

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

~/.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) susudo bash 等のコマンドで root 昇格したとき(su -sudo -i はログインシェル同様の動作)、(3) Pipenv のようなツールでシェルを起動したとき、などが挙げられる。

この場合、次のように各種設定ファイルが実行される:

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

この動作を、次に図示する:

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

つまり *rc 系ファイルだけを実行する。そして *profile 系ファイルは実行されない。また /etc/bashrc という「それっぽい」ファイルを提供している環境もあるけれど、bash 自体にとっては特別なファイルでも何でもない。

3. Non-interactive shell の場合

ログインシェルでも対話シェルでもない形で bash が起動するのは、おそらくシェルスクリプト実行時ぐらいかと思うので、以下はシェルスクリプト実行時を前提に書いていく。この場合、次のように各種設定ファイルが実行される:

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

この動作を、次に図示する:

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

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 系に書くべき内容

  1. ログイン直後に一回だけ実行したい処理
    • uptime でサーバーの連続稼働時間を表示する等
  2. 対話操作には不要だけれどシェルスクリプトの実行には必要な設定
    • 環境変数 LANGJAVA_HOME 等を定義
  3. その他ほとんど変わることの無い設定
    • 環境変数 PATHINCLUDE 等を定義

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

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

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