シェル環境のカスタマイズとは

Bashなどのシェルは、起動するたびに一定のファイルを読み込み、プロンプトの見た目、コマンド検索パス(PATH)、エイリアス、その他の環境変数といった「シェル環境」を初期化します。この初期化に使われるファイル群と、環境変数を扱うための基本的なコマンド(export、set、unsetなど)を理解しておくことは、LPIC-1・LinuCレベル1の出題範囲であるだけでなく、サーバーの運用でユーザーごとの作業環境を整える際にも直結する知識です。

ログインシェルと対話的シェルという2つの軸

画像

設定ファイルがいつ読み込まれるかを理解するには、まずシェルの起動のされ方を2つの軸で捉える必要があります。この2つの軸は独立しており、どちらの組み合わせもあり得ます。

  • ログインシェルかどうか:ログインシェルは、ログイン用として起動されたシェルです。ユーザーが新しくシステムにログインした際に起動されるシェル、コンソールやSSH経由でのログイン直後のシェル、su -(ハイフン付き)で切り替えた際のシェルなどが該当します。Bashでは-lまたは--loginオプションでもログインシェルとして起動できます。一方、GUIのターミナルアプリを開いたときや、既にログイン済みの端末上で単にbashと入力して起動した新しいシェルは、通常は「非ログインシェル」です。ただし、GUIのターミナルアプリの設定や起動方法によってはログインシェルとして起動される場合もあります。
  • 対話的(インタラクティブ)シェルかどうか:プロンプトを表示し、キーボードからの入力を受け付けながらコマンドを実行するシェルが「対話的シェル」です。一方、シェルスクリプトを実行するために起動されるシェル(./script.shなど)は、通常プロンプトを表示せず入力も受け付けないため「非対話的シェル」です。

この2つの軸の組み合わせ(「対話的なログインシェル」「対話的な非ログインシェル」「非対話的なログインシェル」「非対話的な非ログインシェル」)によって、Bashが読み込む設定ファイルの種類が変わります。

起動時に読み込まれる設定ファイル

ログインシェル:/etc/profileと~/.bash_profile系

Bashは、ログインシェルとして起動されると、次の順序でファイルを読み込みます。ただし、--noprofileオプションを付けて起動した場合は、これらのファイルの読み込みは抑制されます。

  • /etc/profile:システム全体(全ユーザー共通)に適用される設定ファイルです。存在し、かつ読み取り可能であれば読み込まれます。
  • ~/.bash_profile・~/.bash_login・~/.profileのうち、存在し、かつ読み取り可能な最初の1つだけ:この3つのファイルは、ユーザー個別の設定ファイルです。Bashはこの順序でファイルの存在と読み取り可否を確認し、最初に該当した1つのファイルだけを読み込みます。3つとも存在していても、後の2つは読み込まれません。

例えば、あるユーザーのホームディレクトリに~/.bash_profileと~/.profileの両方が存在し、どちらも読み取り可能な場合、Bashは~/.bash_profileだけを読み込み、~/.profileは無視します。この「最初に見つかった1つだけ」という仕様は、複数のファイルを用意しているつもりで設定が反映されない、という混乱の元になりやすく、試験でも狙われやすいポイントです。

対話的な非ログインシェル:~/.bashrc

対話的な非ログインシェルとして起動されると、Bashは~/.bashrcを読み込みます(存在すれば)。--norcオプションを付けて起動した場合は、この読み込みが抑制されます。

ログインシェルの終了時:~/.bash_logout

ログインシェルが終了する際には、~/.bash_logoutが存在すれば読み込まれます。ログアウト時に画面をクリアする、一時ファイルを削除するといった後片付けの処理を書いておくためのファイルです。

非対話的シェル:BASH_ENV

シェルスクリプトの実行のように、対話的でないシェルとして起動された場合、Bashは環境変数BASH_ENVの値を確認し、それがファイル名として設定されていれば、そのファイルを読み込みます。BASH_ENVが設定されていなければ、この読み込みは行われません。

/etc/bash.bashrcと/etc/bashrc:ディストリビューションによる慣習の違い

/etc/bash.bashrcはBash本体(アップストリーム)が標準で読み込むファイルではありません。 DebianおよびUbuntuでは、ディストリビューション側のBashのビルド設定により、対話的な非ログインシェルの起動時に、ユーザーごとの~/.bashrcとは別にこの/etc/bash.bashrcをシステム全体の設定として読み込むようにしています。一方、Red Hat系のディストリビューション(Fedora、CentOS/RHEL系など)では、/etc/bash.bashrcではなく/etc/bashrcという名前のファイルが使われ、こちらはBash自身が直接読み込むのではなく、ユーザーの~/.bashrcの中からsourceコマンドで読み込まれるという仕組みになっていますこのように、システム全体の対話的シェル向け設定ファイルの扱いはディストリビューションによって実装方法が異なります。「/etc/bash.bashrcは常にBashが直接読み込む」のように断定せず、「多くのディストリビューションで、こうした仕組みが用意されている」という理解にとどめてください。[1]。

また、多くのディストリビューションが用意する既定の~/.bash_profile(または~/.profile)には、その中で~/.bashrcをsourceする処理があらかじめ書かれています。これは、Bash自体はログインシェルで自動的に~/.bashrcを読み込む仕様にはなっていないため、「ログインシェルでも非ログインシェルと同じエイリアスや関数を使えるようにする」ための一般的な工夫です。

設定ファイルの変更をすぐに反映する:.(ドット)とsource

~/.bashrcなどの設定ファイルを編集しても、その変更は新しく起動したシェルにしか反映されません。既に開いている端末に変更をすぐ反映させたい場合は、.(ドットコマンド)またはsourceコマンドを使います。

source ~/.bashrc
. ~/.bashrc

この2つは同じ意味で、指定したファイルに書かれているコマンドを、新しいプロセス(サブシェル)を作らずに、現在のシェルの中でそのまま実行します。./script.shのようにスクリプトファイルを実行した場合は別プロセスとして実行されるため、そのスクリプト内で設定した変数やcdによるディレクトリ移動は、実行後の元のシェルには反映されません。一方、source(.)で読み込んだ場合は現在のシェルそのものに変更が適用されるため、変数の設定やcdの結果もそのまま残ります。この違いが、設定ファイルの再読み込みにsource(.)を使う理由です。

.はPOSIXで標準化された書き方で、shを含め幅広いシェルで使えます。sourceはBashなど一部のシェルが提供する同義のコマンドで、より意味が分かりやすい書き方として好まれますが、POSIX準拠のshでは使えない場合があります。

Bashのコマンド履歴

Bashでは、対話型シェルで実行したコマンドを、シェル内部の履歴リストに保存しています。保存された履歴は、historyコマンドで確認できます。

通常、Bashの起動時に履歴ファイルが読み込まれ、シェルの終了時に現在の履歴が履歴ファイルへ保存されます。

  • ~/.bash_history:コマンド履歴を保存するデフォルトのファイル
  • HISTSIZE:シェル内部の履歴リストに保持するコマンド数
  • HISTFILESIZE:履歴ファイルに保存する最大行数
history

historyコマンドを実行すると、現在のシェルが保持しているコマンド履歴が、履歴番号とともに表示されます。


HISTSIZEとHISTFILESIZEの違い

変数名対象内容
HISTSIZE現在のシェルの履歴リストシェル内部に保持するコマンド履歴の最大件数
HISTFILESIZE履歴ファイル~/.bash_historyに保存する最大行数

例えば、次のように設定します。

# シェル内部に1,000件の履歴を保持する
HISTSIZE=1000

# 履歴ファイルに最大2,000行を保存する
HISTFILESIZE=2000

これらはBashのシェル変数であるため、通常はexportする必要はありません。

設定を継続して使用する場合は、対話型Bashの設定ファイルである~/.bashrcに記述します。

HISTSIZE=1000
HISTFILESIZE=2000

コマンド履歴が保存されるタイミング

コマンドを実行すると、そのコマンドは現在のシェルが管理する履歴リストに追加されます。

通常は、exitなどによってシェルを終了すると、履歴リストが~/.bash_historyへ保存されます。保存された履歴は、次回Bashを起動したときに読み込まれます。

なお、履歴ファイルへの保存方法にはBashの設定による違いがあるため、必ず「ファイルの末尾へ追記される」とは限りません。LinuCレベル1では、次の関係を押さえておきましょう。

  • history:現在のコマンド履歴を表示する
  • ~/.bash_history:コマンド履歴を保存するファイル
  • HISTSIZE:現在のシェルで保持する履歴数
  • HISTFILESIZE:履歴ファイルに保存する最大行数

コマンドを区切って並べる:リスト(;、&&、||)

Bashでは、複数のコマンドを1行にまとめて書くための区切り方がいくつか用意されています。これらをまとめて「リスト(list)」と呼びます。

  • ;(セミコロン):前のコマンドの成功・失敗に関係なく、順番にコマンドを実行します。
root@miti-shirube:/# echo "Hello" ; echo "World"
Hello
World
root@miti-shirube:/# echoo "Hello" ; echo "World"
bash: echoo: command not found
World
  • &&:直前のコマンドが成功(終了ステータス0)した場合のみ、次のコマンドを実行します。
root@miti-shirube:/# echo "Hello" && echo "World"
Hello
World
root@miti-shirube:/# echoo "Hello" && echo "World"
bash: echoo: command not found
  • ||:直前のコマンドが失敗(終了ステータス0以外)した場合のみ、次のコマンドを実行します。
root@miti-shirube:/# echo "Hello" || echo "World"
Hello
root@miti-shirube:/# echoo "Hello" || echo "World"
bash: echoo: command not found
World

&&・||を使った終了ステータスに応じた条件実行については、既存の技術解説記事「Bashシェルスクリプトの基礎」でより詳しく扱っています。;との最大の違いは、;が前のコマンドの結果を一切考慮しない無条件の区切りであるのに対し、&&・||は前のコマンドの終了ステータスによって次のコマンドを実行するかどうかが変わる条件付きの区切りである点です。

関連して、コマンドの末尾に&を付けると、そのコマンドをバックグラウンドで実行し、終了を待たずに次の処理(プロンプトへの復帰)に進みます。&&(論理AND)と&(バックグラウンド実行)は記号が似ていますが、まったく別の意味を持つため混同しないよう注意してください。

コマンドの別名を定義する:alias

aliasは、よく使うコマンドとオプションの組み合わせに、短い別名(エイリアス)を付けるための仕組みです。

alias ll='ls -alF'
alias grep='grep --color=auto'
  • エイリアスを解除するにはunalias 名前を使います。
  • aliasは引数を渡す関数のような処理はできません。あくまで、各コマンドのコマンド名の位置にある単語を、定義した文字列に単純に置き換えるだけの仕組みです。
  • エイリアスは既定では対話的シェルでのみ有効です。 非対話的シェルでは通常~/.bashrc自体が読み込まれず、エイリアス展開も行われません。シェルスクリプト内でエイリアスを使うには、必要なエイリアスをそのシェルで定義するか定義ファイルを明示的に読み込んだ上で、shopt -s expand_aliasesのように明示的に設定する必要があります。「エイリアスを定義しておけば、スクリプトの中でも同じように動く」という前提でスクリプトを書くと、意図しない動作になる可能性があります。

エイリアスは対話的な操作を手早くするための仕組みであり、~/.bashrc(対話的な非ログインシェル用の設定ファイル)に定義するのが一般的です。

コマンド検索パス:PATH

PATHは、コマンド名だけを入力したときにシェルがどのディレクトリから実行可能ファイルを探すかを決める環境変数です。PATH自体の探索の仕組み(コロン区切り、先頭からの順次検索、カレントディレクトリが既定で含まれない理由など)は、既存の技術解説記事「Bashシェルスクリプトの基礎」で詳しく解説しています。

本記事の観点では、PATHは多くの場合、/etc/profile(システム全体の既定値)や~/.bash_profile・~/.profile(ユーザー個別の追加設定)の中で設定されます。既存の値を上書きしてしまわないよう、次のように既存のPATHの前後に新しいディレクトリを追加する書き方が一般的です。

export PATH="$HOME/bin:$PATH"

この例は、ユーザーのホームディレクトリ内のbinディレクトリを、既存のPATHより優先して検索させたい場合の書き方です(末尾に追加する場合はexport PATH="$PATH:$HOME/bin"のように順序を逆にします)。PATH=$HOME/binのように既存の値を含めずに代入してしまうと、それまで使えていた標準コマンド(lsなど)まで見つからなくなってしまうため注意が必要です。

新規作成ファイルの既定パーミッション:umask

画像

umaskは、新しく作成されるファイルやディレクトリに対して、どの権限をあらかじめ除外するかを設定する仕組みです。

umask 022

多くの一般的な作成処理では、ファイルは最大666(rw-rw-rw-)、ディレクトリは最大777(rwxrwxrwx)の権限を持ち得ます。実際に新規作成されるファイル・ディレクトリの権限は、作成するプログラムが指定する権限を上限として、umaskで指定したビットをビット単位で除外した値になります。

0666 & ~022 = 0644  (rw-r--r--)
0777 & ~022 = 0755  (rwxr-xr-x)

例えばumask 022は、グループ(g)とその他(o)から書き込み権限(w=2)を除外する設定です。作成プログラムが通常の最大権限を指定した場合、新規作成されるファイルは644(rw-r--r--)、ディレクトリは755(rwxr-xr-x)になります。

環境変数を扱う:env、export、set、unset

画像

シェル変数と環境変数の違い

Bashにおける変数には、大きく分けて「シェル変数」と「環境変数」の2種類があります。

  • シェル変数:そのシェルの中だけで有効な変数です。単にVAR=valueと代入しただけの変数はシェル変数であり、VARという名前のコマンドやスクリプトを子プロセスとして実行しても、その子プロセスからは参照できません。
  • 環境変数:シェル変数にexportが付けられた(エクスポートされた)変数です。エクスポートされた変数は、そのシェルから起動される子プロセスに引き継がれ、子プロセス側からも参照できます。

export

exportは、シェル変数に「環境変数として子プロセスに引き継ぐ」という属性を付けるコマンドです。

export EDITOR=vim
EDITOR=vim
export EDITOR

上記の2つの書き方はどちらも同じ結果になります。すでにエクスポート済みの変数の一覧はexport -p(または引数無しのexport)で確認できます。export -n 変数名とすると、変数自体は残したまま、エクスポート属性だけを取り除けます。

env

envは、引数を指定せずに実行すると、現在の環境変数を一覧表示します。コマンドを引数として指定すると、指定した環境で(一時的に環境変数を追加・上書きした状態で)そのコマンドを実行します。

env                       # 現在の環境変数を一覧表示
env VAR=value コマンド     # VARを一時的に設定してコマンドを実行
env -i コマンド            # 環境変数を全て空にした状態でコマンドを実行

envは、シェバン行(#!/usr/bin/env bash)で、インタプリタをPATH経由で検索させる用途でもよく使われます(この用途については「Bashシェルスクリプトの基礎」で解説しています)。

set

setは、シェル変数の表示やシェルの動作設定、位置パラメータの設定など、複数の用途を持つBashの組み込みコマンドです。

使用方法動作・用途
set現在定義されているシェル変数や関数などを表示する。exportと異なり、エクスポートされていないシェル変数も対象となる
set -x実行するコマンドを変数展開後の内容で表示しながら実行する。スクリプトのデバッグに使用する
set -eコマンドが失敗した場合に、原則としてスクリプトを終了する。ただし、ifの条件部や&&・||リストの一部などには例外がある
set -u未定義の変数を参照した場合にエラーとして扱う
set --シェルのオプション処理を終了し、以降の引数を位置パラメータとして扱う
set 値1 値2 ...位置パラメータ($1、$2など)を指定した値に設定する。set --を付けると、値が-で始まる場合も安全に扱いやすい

setはシェル変数や関数など、シェル内で定義されている情報を表示します。一方、exportは環境変数として子プロセスに引き継がれる変数などを扱います。

そのため、setはエクスポートされていないシェル変数も表示するという点が、exportとの主な違いです。

unset

unsetは、変数や関数の定義そのものを削除するコマンドです。

unset VAR

VAR=のように空文字列を代入する操作と、unset VARによって変数自体を削除する操作は、似ているようで異なります。VAR=は「値が空文字列の変数」としてVARが存在し続けるのに対し、unset VARはVARという変数そのものが存在しない状態にします。この違いは、${VAR-デフォルト値}(VARが未定義の場合のみデフォルト値を使う)と${VAR:-デフォルト値}(VARが未定義または空文字列の場合にデフォルト値を使う)という、コロンの有無で意味が変わるパラメータ展開の書き方と組み合わせるとより重要になります。unsetには、変数を対象にする-v、関数を対象にする-fというオプションもあり、同じ名前の変数と関数が両方存在する場合に区別して削除できます。

新規ユーザーの初期設定:/etc/skel(関連トピック)

useraddコマンドで新しいユーザーを作成し、そのホームディレクトリも作成する際、既定では/etc/skelディレクトリの中身が、新しいユーザーのホームディレクトリへそのままコピーされます。ホームディレクトリを作成する-mとともにuseradd -kを指定した場合は、指定したひな形ディレクトリが使われます。ここまで解説してきた~/.bashrcや~/.profileなどの設定ファイルの既定のひな形を/etc/skelに配置しておくことで、新規作成されるすべてのユーザーに、あらかじめ統一された初期設定を適用できます/etc/skelの内容がコピーされるのは、あくまでアカウント作成時点のみです。後から/etc/skelの内容を変更しても、既に作成済みのユーザーのホームディレクトリには自動的に反映されません。[3]。


補足

  1. このように、システム全体の対話的シェル向け設定ファイルの扱いはディストリビューションによって実装方法が異なります。「/etc/bash.bashrcは常にBashが直接読み込む」のように断定せず、「多くのディストリビューションで、こうした仕組みが用意されている」という理解にとどめてください。 ↩
  2. 実際にどのファイルのumask設定が最終的に有効になるかは、ディストリビューションや、PAM(pam_umaskモジュールなど)の設定にも左右されます。「umaskは必ず/etc/profileで設定される」のように特定の1箇所に断定しないよう注意してください。 ↩
  3. /etc/skelの内容がコピーされるのは、あくまでアカウント作成時点のみです。後から/etc/skelの内容を変更しても、既に作成済みのユーザーのホームディレクトリには自動的に反映されません。 ↩