更新情報
- 2019/06/06: 初版公開
- 2021/07/08: 更新
こんにちは、hachi8833です。社内Slackで見かけたmorimorihogeさんの以下の書き込みで目から鱗が落ちました。
~/.bashrcで何かを出力してしまうと、rsyncなどのsshパイプで問題が生じることがあるそうです。
参考:知らないとrsyncでもハマるシェル初期化 - Qiita
これをきっかけに、できるかぎり一次情報を元になるべく一般的になるようにまとめてみました。
シェルスクリプト(.bashrcや.bash_profileなども含む)はあまりに自由に書けてしまい、LinuxディストリビューションやmacOSによって作法がまちまちだったりするので、外してはいけないポイントがどこかを知りたかったのでした。
対象はbashとsh(Bourne Shell)に限定します。また、デスクトップGUIの設定ファイルについては最小限にとどめます。
bashのmanページ
ディストリビューションに依存しない一次資料となるとやはりman bashだろうというアドバイスがmorimorihogeさんからありました。
bash が対話的なログインシェルとして起動されるか、 --login オプション付きの非対話的シェルとして起動されると、
/etc/profileファイルが存在すれば、 bash はまずここからコマンドを読み込んで実行します。 このファイルを読んだ後、 bash は~/.bash_profile,~/.bash_login,~/.profileをこの順番で探します。 bash は、この中で最初に見つかり、かつ読み込みが可能であるファイルから コマンドを読み込んで実行します。
同日本語manページより
bashのmanページは長大かつ非常に詳細です。「どう動くか」についての説明はものすごく詳しいのですが、「どう運用すべきか」についての説明はほとんどありません。
正直、日本語になってても非常に読みづらいのですが、細かな点については最終的にここで確認するのが確実だと思います。
superuser.comの回答
これまたmorimorihogeさんがsuperuser.comの回答の中から「いいね」数の多い回答を抜粋してくれました。
回答1
以下に要点を補足しつつ記します。
- Unixシステムでは「プログラムを起動するためのプログラム」が起動される。これがコマンドラインシェル(いわゆるシェル)と呼ばれる。
- デフォルトのシェルはBourne shell(いわゆる
/bin/sh)と呼ばれ(Bourneは作者の名前)、「ログインシェル」として起動するときに~/.profileを読み込む。 - bash(Bourne Again shell)はBourne shellに似ているがより高機能で、「ログインシェル」として起動すると
~/.bash_profileを読み込む。~/.bash_profileがなければ~/.profileを読み込もうとする。 - シェルがログインシェルではない形で起動する場合は、
~/.profileを読み込まない。 - bashを対話的シェル(=スクリプト実行用ではないということ)として起動するときは、
~/.bashrcを読み込む。
上を踏まえて、次のように使い分けることが勧められています。
| 設定ファイル | 利用法 | 例 |
|---|---|---|
~/.profile | ・ログイン時にそのセッション全体に適用するものを記述する ・シェルの種類に依存しないものを記述する | ・環境変数など |
~/.bashrc | ・bashでしか使わないものを記述する | ・エイリアス ・シェルオプション ・プロンプト設定 |
~/.bash_profile | ~/.profileと同じに使えるが、bashのみで有効 |
- bashを対話的に使う場合、
~/.bash_profileに次を書いておくことを推奨する。.profileがあれば読み込む設定- bashが対話モードであれば
~/.bashrcを読み込む設定
- 「環境変数も
~/.bashrcに書け」とか「ターミナルで常にログインシェルを起動しろ」と言ってる記事がよくあるが、どちらもマズいやり方。- これをやるとターミナル以外から起動するプログラム(GUI起動など)に環境変数が渡らなくなってしまうのが最大の問題。
回答2
こちらも要点を以下にまとめます。
| 設定ファイル | コツ | 例 |
|---|---|---|
~/.profile | ・bashに依存しないものだけを書く ・GUIアプリで使うものやbin/shで使うものはここに置く(必須) ・ログインシェルで使うものはここに置くべき | ・環境変数 ・ PATH変数・etc |
~/.bashrc | ・対話モードで使うものはすべてここに書く ・ここでは何も出力してはならない | ・エイリアス ・ EDITOR変数・プロンプト設定 ・etc |
~/.bash_profile | ・余計なものは極力書かない ・右の順に読み込むだけにする | ・~/.profileがあれば読み込む・ ~/.bashrcがあれば読む |
~/.bash_loginは存在しないようにしておこう
冒頭のスクショと同様、~/.bashrcからは(標準出力や標準エラーに)何も出力してはならないと注意されていますね。
まとめ
上の回答は、設定ファイルの振る舞いや意義を手短に解説しつつ、bash以外でも極力使うにはこう書くのがよいというアドバイスになっています。
そこまで厳密に書かなくても動くのが設定ファイルですし、書き方は人それぞれになると思います。
しかし、少なくとも~/.bashrcで何も出力してはならない、という部分は外さないようにする必要があります。出力があるなら/dev/nullに捨てるなどしましょう。
業務で共有するシェル環境のカスタマイズは最小限にしよう
自分のマシン内のプロファイルで自分だけが使うシェル環境であれば、心ゆくまでカスタマイズして構わないと思います。
逆に、業務で使うLinuxサーバーなどで共有するプロファイルや環境変数については、あまりかっ飛んだカスタマイズをすると他の人がメンテするときに支障が生じる可能性があります。
特に、サーバープロセスで使うユーザーについては不必要なカスタマイズを極力避けたいと思いました。
もちろん、業務で使うサーバーでも、作業者が自分のプロファイルを作って作業する分にはある程度好きにカスタマイズできると思いますが、作業手順が自分のプロファイルに依存すると作業の引き継ぎが面倒になるので、やはりカスタマイズは控えめがよさそうです。
参考
自分のコンフィグ
私も、ローカルVMのUbuntu 18.04.1 Server LTS環境で上のアドバイスに沿って、.profileと.bashrcと.bash_profileを設定しました。
~/.bash_profile: .profileと.bashrcの存在確認&読み込みだけ(何も足さない)~/.profile: 環境変数やGUI設定はここに書く(bashに依存しないもの)~.bashrc: bashに依存する対話モード向け設定はここに書く- 念のため標準出力や標準エラー出力に出力しないよう注意する
- 設定は、.bashrc冒頭の非対話モード脱出コードの後に置く(後述)
~/.bash_alias: (自分で追加)エイリアスはここに書く- (
~/.bash_loginは使っていません)
自分の場合エイリアスの追加頻度が多いので、.bash_aliasという別ファイルを作成してそこに書くようにし、.bashrcで.bash_aliasを存在チェックしつつ読み込むようにしています。
また、macOSのbash設定もこのUbuntuのbash設定と同じ方針で設定し、そこにmacOS固有の設定を追加しました。
追記(2019/06/07)
上に書いた私の設定方針は、自分だけが普段づかいするbash環境向けであり、今後自分の環境でbash以外のシェル(zshとかfishとか)を試してみる可能性や、LinuxデスクトップGUIを試す可能性(しないと思いますが😆)をうっすら考えています。
少なくともデスクトップGUIやbash以外の環境を使いそうにない業務用Linuxサーバーでは、.profileがない場合にわざわざ作成したり、エイリアス用設定ファイルまで置いたりするのはトゥーマッチになることがあるかもしれないと個人的に感じています(プロジェクトでポリシーが決められていれば別)。
参考: Ubuntu Server LTSのデフォルト設定
Ubuntu 18.04.1 Server LTSは、デフォルトでは~/.profileで対話モードかどうかを判断し、対話モードの場合に~/.bashrcを読み込むようになっています。
ただし、~/.bash_profileや~/.bash_loginがある場合は対話時に~/.profileではなくそれらを読み込みます。
# `~/.profile`# if running bashif [ -n "$BASH_VERSION" ]; then # include .bashrc if it exists if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fifiかつ、デフォルトの/etc/bash.bashrcと~/.bashrcの冒頭にも、それぞれ以下の設定が記載されています。
# /etc/bash.bashrc# If not running interactively, don't do anything[ -z "$PS1" ] && return# ~/.bashrc# If not running interactively, don't do anythingcase $- in *i*) ;; *) return;;esacいずれも、bashが対話的でない場合は即脱出し、それより後の行なら何を書いても決して出力されることのないようになっています。
つまり、~/.bashrcが非対話モードで誤って何かを出力しないよう、二重に対策が行われています。
参考: CentOS 7のデフォルト設定
CentOS Linux release 7.5.1804 (Core)の場合、以下のようになっていました。
CentOSでは、従来よくある方式と同様、~/.bash_profileで~/.bashrcが存在するかどうかを調べ、存在すれば読み込むようになっています。
# ~/.bash_profile# Get the aliases and functionsif [ -f ~/.bashrc ]; then . ~/.bashrcfi一方、/etc/bashrcでは、対話的な場合にはプロンプトの設定などを行っていますが、非対話の場合の脱出は特に行っていません。
# /etc/bashrc# are we an interactive shell?if [ "$PS1" ]; then if [ -z "$PROMPT_COMMAND" ]; then case $TERM in xterm*|vte*) if [ -e /etc/sysconfig/bash-prompt-xterm ]; then PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm elif [ "${VTE_VERSION:-0}" -ge 3405 ]; then PROMPT_COMMAND="__vte_prompt_command" else PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"' fi ;; screen*) if [ -e /etc/sysconfig/bash-prompt-screen ]; then PROMPT_COMMAND=/etc/sysconfig/bash-prompt-screen else PROMPT_COMMAND='printf "\033k%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"' fi ;; *) [ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=/etc/sysconfig/bash-prompt-default ;; esac fi # Turn on parallel history shopt -s histappend history -a # Turn on checkwinsize shopt -s checkwinsize [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ " # You might want to have e.g. tty in prompt (e.g. more virtual machines) # and console windows # If you want to do so, just add e.g. # if [ "$PS1" ]; then # PS1="[\u@\h:\l \W]\\$ " # fi # to your custom modification shell script in /etc/profile.d/ directoryfiかつ、~/.bashrcは単に/etc/bashrcに丸投げしています。
# ~/.bashrc# Source global definitionsif [ -f /etc/bashrc/ ]; then . /etc/bashrcfi非対話かつログインでないシェルであれば/bin/shが動くから~/.bashrcも~/.bash_profileも読み込まれないはずなので、Ubuntuのように脱出の条件までは書かないという考え方なのかなと推測しました。
おたより発掘
良いまとめ。今は自分も人に教えることが多いので.profileとかに毎回環境変数やエイリアスだけをまとめて、極力激しいシェルカスタマイズはしないようにしてるなぁ。昔はゴリゴリにいじってたけど。 / “Linux: .bashrcと.bash_profileの違いを今度こそ理解する”https://t.co/aTI3wvkaOy
— sifue(吉村 総一郎) (@sifue)June 6, 2019
関連記事
hachi8833
X:@hachi8833GitHub:@hachi8833コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。これまでにRuby on Rails チュートリアル第2版のコンテンツ監修、Railsガイドのコンテンツ作成を担当。かと思うと、正規表現の粋を尽くした日本語エラーチェックサービスenno.jpを運営。Claude Codeに夢中になりすぎないための方法を模索中。ブログ:note.com/hachi8833、Amazonウィッシュリスト:https://bit.ly/32aAmiI
hachi8833の書いた記事一覧へ
本記事の内容へのお問い合せはTwitterで@techrachoへMentionまたはDMにてご連絡頂くか、運営会社であるBPS株式会社のお問い合せフォームよりお問い合せ下さい。
Events
Category New Posts
- Ruby / Rails以外の開発一般
エンジニア組織の中間管理職がAI活用で1年間試行錯誤してみた
- Ruby / Rails以外の開発一般
ChatGPTのしくみとAI理論の根源に迫る:(13/16)ChatGPTは実際にどう動いているのか(翻訳)
- Ruby / Rails以外の開発一般
CSS: flexコンテナでは常に`flex-wrap: wrap`を指定しよう(翻訳)
- Ruby / Rails以外の開発一般
AI: 待ちに待った Kiro を使ってみたら想像以上に強力だった
- Ruby / Rails以外の開発一般
ChatGPTのしくみとAI理論の根源に迫る:(12/16)基本的なトレーニングの次に何をするか(翻訳)














