LoginSignup
4
7

bashの .bash_profile と .bashrc の挙動の整理と使い分け方

Last updated at Posted at 2023-05-05

新しい開発環境を構築していて、 .bashrc.bash_profile のどちらに何を書くべきなのか、今までは適当にやっていたがベストプラクティスを調べて準拠したくなり、気合を入れて調べたのでメモを残す。(macOS Venturaで確認)

調べたいこと

  • bashの主要な設定ファイルである .bashrc.bash_profile は、それぞれどんなときに読み込み・実行されるのか確認し、整理する
  • 上記整理に従って、bashの設定の管理指針の明確化を図る

bashシェルのモード

bashのシェルには、起動方法によって「ログインシェル」と「インタラクティブシェル」があり、それぞれ読み込まれる設定ファイルが異なる。

ログインシェル」は、主にシェルのセッション開始時に用いられる。具体的には、macOSでターミナルを立ち上げたり、SSHでリモートログインしたときなどである。一番「外側のシェル」と考えておけば良いだろう。逆に、シェルの中でさらにbashコマンドなどでシェルを開始した場合などは「非ログインシェル」となる。

インタラクティブシェル」は、ユーザーがインタラクティブに操作可能な(具体的には標準入力と標準エラー出力がターミナルに接続された)状態で起動したシェルを指す。逆に、bash -c datebash foo.sh のように、コマンドやシェルスクリプトを実行する場合は「非インタラクティブシェル」となる。

これらふたつの概念は直交しているので、ログインシェルかつインタラクティブシェル(インタラクティブ・ログインシェル)、あるいは非ログインシェルかつ非インタラクティブシェル(非インタラクティブ・非ログインシェル)という状態も取りうる。

確認方法

そのシェルが ログインシェル であるかどうかは、shopt -q login_shell の終了コードが0かどうかで判別できる。また、インタラクティブシェル であるかを確認するには、bashの起動フラグが格納されている $- 変数に i が含まれているかどうかで判別できる。

$ bash --login -i      # ログインシェルかつインタラクティブシェルでbashを起動
$ echo $-
himBH                  # iが含まれている => インタラクティブシェル
$ shopt -q login_shell
$ echo $?
0                      # 直前の終了コードが0 => ログインシェル

各起動モードでの設定ファイルの実行パターン

さて、manによれば、上記のそれぞれのシェルでの設定ファイルの読み込み・実行パターンは以下の定義されている。

モード 設定ファイルの実行
インタラクティブ・
ログインシェル
① /etc/profile を実行する
② ~/.bash_profile, ~/.bash_login, ~/.profile の順で最初に見つかったファイルひとつを実行する
インタラクティブ・
非ログインシェル
~/.bashrc を実行する
非インタラクティブシェル デフォルトでは何も実行しない($BASH_ENV があれば、指定された名前のファイルを実行する)

各設定ファイルごとの早見表にすると、こうなる。

インタラクティブ・ログインシェル インタラクティブ・非ログインシェル 非インタラクティブシェル
/etc/profile
~/.bash_profile
~/.bash_login
~/.profile
いずれかひとつ
~/.bashrc
$BASH_ENV

アプリケーション経由でbashを起動するときの挙動

次に、各アプリケーションや環境でbashを使った時に、実際にどのモードが使われるか確認していく。

macOSのTerminal.app

macでターミナルを立ち上げると、 インタラクティブ・ログインシェル が開始する。読み込まれるのは .bash_profile

SSH

SSHリモートログイン

SSHでリモートホストにログインすると、リモートホストのbashは インタラクティブ・ログインシェル が開始する。読み込まれるのは .bash_profile

SSHリモートコマンド/SCP

SSHリモートコマンド実行(例: ssh localhost hostname、またはscp)の際にはリモートホストで 非インタラクティブ・非ログインシェル が開始する。

しかし、bashはリモートシェルでの実行を検知すると .bashrcを実行する仕様になっている。

(man bash)
Bash attempts to determine when it is being run by the remote shell daemon, usually rshd. If bash determines it is being run by rshd, it reads and executes commands from ~/.bashrc, if that file exists and is readable.

Bashは、リモートシェルデーモン(通常はrshd)によって実行されているかどうかを判断しようとします。bash が rshd によって実行されていると判断した場合、~/.bashrc が存在し、そのファイルが読める場合は、そのファイルからコマンドを読み込んで実行する。

この仕様によって、 .bashrcにechoが含まれていると問題が起きるので注意すること。

screen

screenで新規ウィンドウを作成すると、 インタラクティブ・ログインシェルが開始する。実行されるのは .bashrcである。

tmux

tmuxで新規ペインを作成すると、デフォルトでは インタラクティブ・ログインシェル が開始する。そのため、実行されるのは .bash_profile である。

後で述べるように、この挙動はやや都合が悪いので、 .tmux.conf に以下の設定を追加することで インタラクティブ・非ログインシェル に変更することができる。こうすると .bashrcが実行されるようになる。

.tmux.conf
set -g default-command "${SHELL}"

管理方針

ここまでで、bashの主な起動モードや実行される設定ファイル、各アプリケーションからシェルを立ち上げたときの挙動を大まかに確認できた。

インタラクティブ・ログインシェル (.bash_profile) インタラクティブ・非ログインシェル(.bashrc) 非インタラクティブシェル(実行なし)
macOS Terminal新規ウィンドウ
GNOMEデスクトップログイン時(らしい)
SSHリモートログイン
SSHリモートコマンド,SCP ○(※1)
bash(シェル起動)
bash(コマンド実行)
screen
tmux
(デフォルト時)

(default-command設定時)

※1: モード自体は非インタラクティブだが、前述の通りbashのリモートシェル仕様によってインタラクティブ相当の動作をする

では、これを踏まえてbashの設定ファイルの管理方針を検討しよう。

一般的に、.bash_profileにはそのセッションにおいて一度だけ実行したい設定を、 .bashrc にはシェルを開始するごとに毎回実行したい設定を記述するべきと言われている。

シェルの中でシェルを開始した場合、環境変数は内側のシェルに引き継がれる。そのため、シェルを起動するごとに実行される .bashrcexport PATH=/path/to/bin:$PATHといった環境変数の追加を記述してしまうと、シェルを立ち上げるごとにPATHが長くなってしまう。それで誤動作が起きるケースは稀だが、ともあれそういった理由で環境変数の設定の記述は .bash_profile が適しているとされている。

一方で、シェルのエイリアスや関数の定義などは、内側のシェルには引き継がれない。これらの設定を ログインシェルでしか実行されない .bash_profile に記述してしまうと、内側のシェルでエイリアスなどが使えなくなってしまうので、エイリアスや関数は .bashrcで定義するのが適しているとされている。

この一般的な方針を採用しても問題なさそうである。ただし、二つだけ注意することがある。ひとつは、単純にそのように書き分けただけではターミナルを立ち上げた(ログインシェル)際に .bashrc が実行されず、エイリアスや関数が定義されないので、.bash_profile から .bashrc をsourceしてやる必要がある。

.bash_profile
[[ -f ~/.bashrc ]] && source ~/.bashrc

もう一つはtmuxである。先ほど少し述べたように、デフォルトではtmuxはインタラクティブ・ログインシェルを起動してしまう。しかし、ユーザーは通常 bash などのシェルから tmux を開始するので、これではログインシェルが2階層になってしまう。これによって、本来シェルのセッション中一度だけ実行したい .bash_profile が2回実行され、PATHが長くなってしまうなどの問題として現れる。そこで、インタラクティブ・ノンログインシェル を起動させるため、 tmux.conf で設定を変更する必要がある。

.tmux.conf
set -g default-command "${SHELL}"

これで「.bash_profileはセッション開始時に最初に一度だけ実行される、.bashrcはシェル開始時に毎回実行される」という原則へのコンプライアンスが得られた。

まとめ

.bash_profileはセッション開始時に一度だけ実行される。 .bashrcはシェル開始時に毎回実行される。(ただしtmuxのように一部この原則に準拠しないアプリケーションがあるので対処が必要)

本稿で私が勧めるbashの設定ファイルの管理方針は、.bash_profileで環境変数を設定して.bashrcをsourceする、 .bashrc でエイリアスや関数を設定すること

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7