bash起動ファイルメモ

概要

manの結果を読んでみたけどぱっとみよくわからないので整理した。

基本的にDokcer(amazonlinux:2)、一部VirtualBox+CentOS7で検証した。

事前準備

次のDockerfileでDockerイメージを作成し、それを使う。

FROM amazonlinux:2

RUN \
  yum install -y procps sudo gcc man && \
  echo 'echo /etc/profile && echo $- && shopt login_shell'  >> /etc/profile && \
  echo 'echo .bash_profile && echo $- && shopt login_shell' >> ~/.bash_profile && \
  echo 'echo .bash_login && echo $- && shopt login_shell'   >> ~/.bash_login && \
  echo 'echo .profile && echo $- && shopt login_shell'      >> ~/.profile && \
  echo 'echo .bashrc && echo $- && shopt login_shell'       >> ~/.bashrc && \
  echo 'echo .bash_logout && echo $- && shopt login_shell'  >> ~/.bash_logout && \
  touch ~/.bash_env && echo 'echo .bash_env && echo $- && shopt login_shell' >> ~/.bash_env && \
  touch ~/.env && echo 'echo .env && echo $- && shopt login_shell' >> ~/.env && \
  useradd mfham -s /bin/bash && \
  echo 'echo mfham_.bash_profile && echo $- && shopt login_shell' >> /home/mfham/.bash_profile && \
  echo 'echo mfham_.bash_login && echo $- && shopt login_shell'   >> /home/mfham/.bash_login && \
  echo 'echo mfham_.profile && echo $- && shopt login_shell'      >> /home/mfham/.profile && \
  echo 'echo mfham_.bashrc && echo $- && shopt login_shell'       >> /home/mfham/.bashrc && \
  echo 'echo mfham_.bash_logout && echo $- && shopt login_shell'  >> /home/mfham/.bash_logout

ENV BASH_ENV=~/.bash_env
ENV ENV=~/.env

用語

ログインシェル(login shell)

  • 0番目の引き数の最初の文字が - であるシェル
  • --login オプション付きで起動されたシェル

対話的なシェル(interactive shell)

  • オプションでない引き数がなく、標準入力と標準エラー出力がいずれも端末に接続されていて(isatty(3)で調べられる)、-cオプションが指定されていない状態で起動されたシェル
  • -i オプション付きで起動されたシェル

非対話的なシェル

manの結果に定義が記載されていないので、対話的なシェルでないものと認識しておく。

bash

bashが対話的なログインシェルとして起動されるか、--loginオプション付きの非対話的シェルとして起動される

/etc/profileファイルが存在すれば、ここからコマンドを読み込んで実行する。
その後、~/.bash_profile, ~/.bash_login, ~/.profile の順番で探す。この中で最初に見つかり、かつ読み込みが可能であるファイルからコマンドを読み込んで実行する(最初に見つかったものだけ)。

また、--noprofileオプションを使ってシェルを起動すれば、/etc/profile~/.profile読み込みの動作を行わないようにできる。

非対話的なログインシェル(--loginオプションを使わない)

man bashの記述的に「非対話的なログインシェル(--loginオプションを使わない)」は/etc/profileを読み込まないと思っていたけど読み込んだ。
Difference between Login Shell and Non-Login Shell? - Unix & Linux Stack ExchangeのAnswer的に、システムによって読まれたり読まれなかったりするのかな?

ログインシェルが終了する

~/.bash_logout ファイルがあれば読み込んで実行する。

ログインシェルでない対話的シェルとして起動される

~/.bashrc ファイルがあれば、ここからコマンドを読み込み実行する。

--norcオプションを使ってシェルを起動すれば、~/.bashrc読み込みの動作を行わないようにできる。 --rcfile fileオプションを使うと、~/.bashrcの代わりにfileから読み込ませることができる。

(例えばシェルスクリプトを実行するために)非対話的に起動される

環境変数 BASH_ENV を調べ、この変数が定義されていればその値を展開し、得られた値をファイル名とみなして、そこからコマンドの読み込みと実行を行う。
つまり次のコマンドが実行されたのと同じように動作する。
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
ただし、ファイル名を探すためにPATH環境変数の値が使われることはない。

--posixコマンドラインオプション等によりPOSIXモードで起動される

起動ファイルに関してPOSIX標準に従う。
このモードでは、対話的シェルはENV環境変数を展開し、展開して得られた名前のファイルからコマンドの読み込みと実行を行う。
ほかの起動ファイルは全く読み込まない。

リモートシェルデーモンrshdやセキュアシェルデーモンsshdによって実行された場合

~/.bashrcが存在し、かつ読み込み可能であれば、bashはコマンドをこのファイルから読み込んで実行する。 shとして呼び出された場合には、この動作は行わない。
--norcオプション、--rcfileオプションも使うことが可能。
(しかし一般的にはrshdはこれらのオプションを付けてシェルを起動しないし、指定もできないようになっている)

# VirtualBox+CentOS7での検証
mfham@localhost ~> sudo useradd mfham_test
[sudo] mfham のパスワード:

# backup
mfham@localhost ~> sudo cp /etc/profile /etc/profile_origin

# 事前準備1
mfham@localhost ~> sudo su -
[root@localhost ~]# echo 'echo /etc/profile' >> /etc/profile && echo 'echo $-' >> /etc/profile && echo 'shopt login_shell' >> /etc/profile

# 事前準備2
[root@localhost ~]# su mfham_test
[mfham_test@localhost ~]$ echo $SHELL
/bin/bash
[mfham_test@localhost root]$ cd
[mfham_test@localhost ~]$ echo 'echo bash_profile' >>~/.bash_profile && echo 'echo bash_login' >> ~/.bash_login && echo 'echo profile' >> ~/.profile && echo 'echo bashrc' >> ~/.bashrc && echo 'echo bash_logout' >> ~/.bash_logout

# ログイン
mfham@mac ~> ssh cent7_test
mfham_test@****'s password:
Last login: Sat Jan  5 05:48:17 2019
/etc/profile
himBH
login_shell     on
bashrc
bash_profile
[mfham_test@localhost ~]$ ps aux | grep mfham_test
root     10025  0.3  0.1 159300  6112 ?        Ss   05:57   0:00 sshd: mfham_test [priv]
mfham_t+ 10027  0.4  0.0 159300  2352 ?        S    05:57   0:00 sshd: mfham_test@pts/0
mfham_t+ 10077  0.0  0.0 112724   988 pts/0    R+   05:58   0:00 grep --color=auto mfham_test

# ログインシェルをshに変更
[mfham_test@localhost ~]$ chsh
mfham_test のシェルを変更します。
新しいシェル [/bin/bash]: /bin/sh
パスワード:
シェルを変更しました。
[mfham_test@localhost ~]$
# ログアウト後にログインしなおす
mfham@mac ~> ssh cent7_test
mfham_test@****'s password:
Last login: Sat Jan  5 05:52:10 2019 from ****
/etc/profile
himBH
login_shell     on
profile
-sh-4.2$

シェルが実ユーザ (グループ)IDと異なる実効ユーザ(グループ)IDで起動される

  • -pオプションが与えられていない場合
    • 起動ファイルは全く読み込まれず、シェル関数は環境から継承されず、SHELLOPTS,BASHOPTS,CDPATH,GLOBIGNOREが環境変数に含まれていても無視され、実効ユーザIDには実ユーザIDが設定される。
  • -pオプションが起動時に与えられた場合
    • 起動時の動作は同じだが、実効ユーザIDは再設定される。

参考:

# mfhamユーザでログイン、ログアウト
bash-4.2# su - mfham
/etc/profile
himBH
login_shell     on
mfham_.bashrc
himBH
login_shell     on
mfham_.bash_profile
himBH
login_shell     on
[mfham@6ac0fadafe8d ~]$
[mfham@6ac0fadafe8d ~]$ exit
logout
mfham_.bash_logout
himBH
login_shell     on
bash-4.2#

# bashのパーミッション変更
bash-4.2# ls -l /bin/ | grep bash
-rwxr-xr-x 1 root root  964728 Jul 27 18:42 bash
lrwxrwxrwx 1 root root      10 Nov 14 07:22 bashbug -> bashbug-64
-rwxr-xr-x 1 root root    6958 Jul 27 18:42 bashbug-64
lrwxrwxrwx 1 root root       4 Nov 14 07:22 sh -> bash
bash-4.2# chmod u+s /bin/bash
bash-4.2# ls -l /bin/ | grep bash
-rwsr-xr-x 1 root root  964728 Jul 27 18:42 bash
lrwxrwxrwx 1 root root      10 Nov 14 07:22 bashbug -> bashbug-64
-rwxr-xr-x 1 root root    6958 Jul 27 18:42 bashbug-64
lrwxrwxrwx 1 root root       4 Nov 14 07:22 sh -> bash
bash-4.2#

# 再度mfhamユーザでログイン
bash-4.2# su - mfham
Last login: Mon Jan 14 12:48:52 UTC 2019 on pts/0
-bash-4.2$
-bash-4.2$ pwd
/home/mfham
-bash-4.2$

# 実ユーザID、実効ユーザID確認用スクリプト作成
-bash-4.2$ vi test.c
-bash-4.2$ cat test.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    printf("uid = %d, euid = %d\n", getuid(), geteuid());
    getchar();
    return 0;
}

-bash-4.2$
-bash-4.2$ gcc test.c
-bash-4.2$ ls -l
total 16
-rwxr-xr-x 1 mfham mfham 8328 Jan 14 12:52 a.out
-rw-r--r-- 1 mfham mfham  166 Jan 14 12:51 test.c
-bash-4.2$

# 確認
-bash-4.2$ ./a.out
uid = 1000, euid = 1000
^C
-bash-4.2$

# -pオプションつけてのbash起動後に確認
-bash-4.2$ bash --login -p
bash-4.2# ./a.out
uid = 1000, euid = 0
^C
bash-4.2#

確認

# 対話的なログインシェル
bash-4.2# bash --login
/etc/profile
himBH
login_shell     on
.bash_profile
himBH
login_shell     on
bash-4.2#

# --loginオプション付きの非対話的シェル
bash-4.2# bash --login -c 'echo foo'
/etc/profile
hBc
login_shell     on
.bash_profile
hBc
login_shell     on
.bash_env
hBc
login_shell     on
foo
bash-4.2#

# 非対話的なログインシェル(--loginオプションを使わない)
bash-4.2# ln -s /bin/bash /bin/-bash
bash-4.2# -bash -c 'echo foo'
/etc/profile
hBc
login_shell     on
.bash_profile
hBc
login_shell     on
.bash_env
hBc
login_shell     on
foo
bash-4.2#

# --noprofileオプションを使って起動
bash-4.2# bash --login --noprofile
bash-4.2#

# logout
bash-4.2# bash --login
/etc/profile
himBH
login_shell     on
.bash_profile
himBH
login_shell     on
bash-4.2# exit
logout
.bash_logout
himBH
login_shell     on
bash-4.2#

# ログインシェルでない対話的シェルとして起動
bash-4.2# bash -i
.bashrc
himBH
login_shell     off
bash-4.2#

# ログインシェルでない対話的シェルとして起動(--norcオプション)
bash-4.2# bash --norc  -i
bash-4.2#

# ログインシェルでない対話的シェルとして起動(--rcfileオプション)
bash-4.2# touch tmp_file && echo 'echo tmp_file && echo $- && shopt login_shell' >> tmp_file
bash-4.2# bash --rcfile tmp_file -i
tmp_file
himBH
login_shell     off
bash-4.2#

# 非対話的に起動
bash-4.2# bash -c 'echo foo'
.bash_env
hBc
login_shell     off
foo
bash-4.2#

# 対話的なログインシェル(--posixオプション)
bash-4.2# bash --login --posix
.env
himBH
login_shell     on
bash-4.2#

shという名前でbashを起動

bashは古くからあるshの起動動作をできるだけ真似しようとし、またPOSIX標準にもできるだけ従おうとする。

参考:

# shはbashのalias
bash-4.2# ls -l /bin/ | grep sh
-rwxr-xr-x 1 root root 964728 Jul 27 18:42 bash
lrwxrwxrwx 1 root root     10 Nov 14 07:22 bashbug -> bashbug-64
-rwxr-xr-x 1 root root   6958 Jul 27 18:42 bashbug-64
lrwxrwxrwx 1 root root     19 Nov 14 07:22 setup-nsssysinit -> setup-nsssysinit.sh
-rwxr-xr-x 1 root root   1539 Oct  3 18:20 setup-nsssysinit.sh
lrwxrwxrwx 1 root root      4 Nov 14 07:22 sh -> bash
-rwxr-xr-x 1 root root  37368 Jul 31 20:17 sha1sum
-rwxr-xr-x 1 root root  37384 Jul 31 20:17 sha224sum
-rwxr-xr-x 1 root root  37384 Jul 31 20:17 sha256sum
-rwxr-xr-x 1 root root  37400 Jul 31 20:17 sha384sum
-rwxr-xr-x 1 root root  37400 Jul 31 20:17 sha512sum
-rwxr-xr-x 1 root root  54104 Jul 31 20:17 shred
-rwxr-xr-x 1 root root  50216 Jul 31 20:17 shuf

# 起動ファイルの読み込みを行った後にPOSIXモードに入る
bash-4.2# bash -c 'echo <(ls)'
/dev/fd/63
bash-4.2# sh -c 'echo <(ls)'
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `echo <(ls)'

対話的なログインシェルとして起動される、あるいは--loginオプション付きの非対話的シェルとして起動される

まず/etc/profile~/.profileの順でコマンドの読み込みと実行をしようとする。
また、--noprofileオプションを使ってシェルを起動すれば、/etc/profile~/.profile読み込みの動作を行わないようにできる。

対話的シェルとして起動される

環境変数ENVを調べ、この変数が定義されていればその値を展開し、展開で得た値をコマンドの読み込みと実行を行うためのファイル名として使う。
また、ほかの起動ファイルからコマンドの読み込みと起動を行うことはないので、--rcfileオプションは全く効果を持たない。

非対話的シェルとして起動される

ほかの起動ファイルを何も読み込まない。
(bashという名前で非対話的シェルとして起動した場合は、先述の通りBASH_ENVの参照)

確認

# 対話的なログインシェル
bash-4.2# sh --login
/etc/profile
himBH
login_shell     on
.profile
himBH
login_shell     on
.env
himBH
login_shell     on
sh-4.2#

# --loginオプション付きの非対話的シェル
bash-4.2# sh --login -c 'echo foo'
/etc/profile
hBc
login_shell     on
.profile
hBc
login_shell     on
foo
bash-4.2#

# 非対話的なログインシェル(--loginオプションを使わない)
bash-4.2# ln -s /bin/bash /bin/-sh
bash-4.2# -sh -c 'echo foo'
/etc/profile
hBc
login_shell     on
.profile
hBc
login_shell     on
foo
bash-4.2#

# --noprofileオプションを使って起動
bash-4.2# sh --login --noprofile
.env
himBH
login_shell     on
sh-4.2#

# ログインシェルでない対話的シェル
bash-4.2# sh -i
.env
himBH
login_shell     off
sh-4.2#

# 対話的なログインシェル
bash-4.2# sh --login
/etc/profile
himBH
login_shell     on
.profile
himBH
login_shell     on
.env
himBH
login_shell     on
sh-4.2#

# --rcfileオプションを使って起動
bash-4.2# touch tmp_file && echo 'echo tmp_file && echo $- && shopt login_shell' >> tmp_file
bash-4.2# sh --login --rcfile tmp_file
/etc/profile
himBH
login_shell     on
.profile
himBH
login_shell     on
.env
himBH
login_shell     on
sh-4.2#

# 非対話的シェルとして起動
bash-4.2# sh -c 'echo foo'
foo
bash-4.2#

まとめ

  • 知らぬも困らぬが役に立つ
  • /etc/profileの中身もあわせて理解しておきたい
  • こういう検証にDocker使うの便利
  • 私は最近fishシェルを使っている