Docker コンテナ等でよく使われる alpine linux. そのログインシェルである ash
の設定ファイルについてです。
私自身 Docker 以外で alpine linux をよく使っています。(参考: iSH)
Docker を前提とした解決方法以外で、設定ファイルについて書かれた日本語の記事が(私が探した限りでは)なかったので記述します。
忙しい人向け
-
ash
にはデフォルトで.bashrc
に相当する対話シェル用の設定ファイルはありません。 -
$HOME/.profile
を作成し、その中に以下のものを記述することで、.bashrc
相当のファイル(以下だと.ashrc
という名前)を作ることができます。(対話シェル用設定ファイルの名前は.ashrc
である必要はなく、任意の名前が使えます。) - ただしこれだと対話シェル以外(シェルスクリプト実行時など)にも読み込まれるため、一工夫が必要です(後述)。
ENV=$HOME/.ashrc; export ENV
解説
結構探し回ったのですが、ash
の man に答えがちゃんとありました。 (iSH
にはそもそも man
がデフォルトで存在しないので見逃してた)
ネット上以外で man ash(1)
を手に入れることができなかったので、真偽は定かではありません。
ただし man busybox(1)
は docker の alpine コンテナで手に入ったのでそれを見ようと思います。
と思ったら、 busybox(1) の man と以下の文言は全く別のものだった(どちらかと言ったら dash
の方が近い)。 ただ ash --help
と busybox --help
の出力が違うのは謎。readlink -f $(command -v ash)
と readlink -f $(command -v busybox)
はどちらも /bin/busybox
なのに ...
ですがとりあえず各所で根拠として挙げられている ash(1)
の man
ページを示します。
Invocation
If no args are present and if the standard input of the shell is connected to a terminal (or if the -i flag is set), and the -c option is not present, the shell is considered an interactive shell. An interactive shell generally prompts before each command and handles programming and command errors differently (as described below). When first starting, the shell inspects argument 0, and if it begins with a dash '-', the shell is also considered a login shell. This is normally done automatically by the system when the user first logs in. A login shell first reads commands from the files /etc/profile and .profile if they exist. If the environment variable ENV is set on entry to a shell, or is set in the .profile of a login shell, the shell next reads commands from the file named in ENV. Therefore, a user should place commands that are to be executed only at login time in the .profile file, and commands that are executed for every shell inside the ENV file. To set the ENV variable to some file, place the following line in your .profile of your home directory
ENV=$HOME/.shinit; export ENV
意訳:
起動
引数なしでシェルが起動され、かつ標準入力がターミナルに接続されていたら(もしくは
-i
オプションが設定されている)、かつ-c
オプションでない場合、シェルはインタラクティブシェル(訳注: 対話モードとログインシェルとは 参照)だよ。インタラクティブシェルは一般的にプロンプトがあり、プログラミングやエラーハンドリングが異なるよ。
はじめにシェルは0番目の引数(訳注:$0
, 自分自身の文字列のこと) を見るよ。-
で始まってたらログインシェルだよ。この処理はシステムにより最初のログイン時に自動で行われるよ。ログインシェルは最初に/etc/profile
と.profile
を読むよ。シェル実行時にはENV
環境変数があれば(もしくは.profile
中で設定されれば)、シェルは次にそのENV
環境変数で指定されたファイルを読むよ。よってユーザは ログイン時に実行したいものは.profile
に、シェル実行時に毎回実行したいものはENV
で指定されたファイルに書くべきだよ。ENV
環境変数を設定して実行させる場合には.profile
に以下のように書いてね。ENV=$HOME/.shinit; export ENV
というわけなので ログイン時に実行したいものは .profile
に、シェル実行時に毎回実行したいものは ENV
で指定されたファイルに書きましょう(ただし実際には違う挙動をする(後述))。
しかしこの章には続きがあります。
[省略] Since the ENV file is read for every invocation of the shell, including shell scripts and non-interactive shells, the following paradigm is useful for restricting commands in the ENV file to interactive invocations. Place commands within the ''case'' and ''esac'' below (these commands are described later):
case $- in *i*) # commands for interactive use only ... esac
意訳:
ENV
で指定されたファイルはどんなシェルの呼び出しでも読まれるよ。それにはシェルスクリプトや非対話シェルも含まれるよ。なのでインタラクティブな時だけ実行するなら以下のやり方をお勧めするよ ...
ですがこの挙動は手もとでは確認できませんでした。なので基本は case 句で囲わなくて良いかもしれません。
ただし、検証の結果、.profile
は対話シェルかつログイン時でのみ実行されるのに対して、 ENV
で指定したファイルは 対話シェルであればログインシェル・非ログインシェル両者 の場合に読み込まれることがわかりました。つまり .bashrc
を .profile
で読み込んだときと同じ挙動をすることがわかりました。
これらの挙動は sh
と似ていると言えるのではないでしょうか。
ENVIRONMENT VARIABLES
The following environment variables shall affect the execution of sh:
ENV
This variable, when and only when an interactive shell is invoked, shall be subjected to parameter expansion (see Parameter Expansion ) by the shell, and the resulting value shall be used as a pathname of a file containing shell commands to execute in the current environment. The file need not be executable. If the expanded value of ENV is not an absolute pathname, the results are unspecified. ENV shall be ignored if the real and effective user IDs or real and effective group IDs of the process are different.
挙動の検証
挙動の確認には .ashrc
/ .profile
に以下のように書き込み、以下を実施した後、printenv
で確認しました。
DOT_ASHRC="$(date)"
export DOT_ASHRC
DOT_PROFILE="$(date)"
export DOT_PROFILE
- 実施したこと
-
ash -c true
=>DOT_PROFILE
/DOT_ASHRC
両者変化なし。 -
hello
ファイルを作成し$ ash ./hello
/$ ./hello
を実行した =>DOT_PROFILE
/DOT_ASHRC
両者変化なし。-
hello
#!/bin/sh echo "hello"
-
-
$ ash
を実行した (対話シェル
かつ非ログインシェル
) =>DOT_ASHRC
のみ変化 - 非ログインシェルの状態で先ほどの
hello
を実行する($ ./hello
/$ ash ./hello
) =>DOT_PROFILE
/DOT_ASHRC
両者変化なし。
-
環境変数DOT_ASHRC
は変化せず、非対話シェルで実行したものでは .ashrc
は読み込まれていませんでした。
ただし $ ash
を実行した時のように 対話シェル
かつ 非ログインシェル
では実行されていました。(.profile
は実行されず、DOT_PROFILE
は変化しなかった。)
これらは以下の環境変数で実証しました。
# iSH
$ cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.14.3
PRETTY_NAME="Alpine Linux v3.14"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
# docker alpine:3.14
$ cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.14.10
PRETTY_NAME="Alpine Linux v3.14"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
そもそもの設定ファイル類について(おまけ)
まずそもそもの設定ファイル類について bash
を例に簡単に説明します。
そんなもん要らん! という人は読み飛ばしてください。
種類と読み込み順
主な設定ファイルには .bashrc
と .bash_profile
, .profile
があり、それぞれ違いがあります。これらのファイルの中にシェルプログラムを書き込むことで、指定されたときに読み込み(大概何かの開始時)。設定をすることができます。
-
~/.bashrc
は対話モードかつ非ログインシェルの bash を起動する時に毎回実行。-
$ bash
のように新たに対話シェルで bash を実行するときのみ実行する。逆にそれ以外でbash
自身が読み込むことはない。(ログイン時に自動的に読み込まれるわけではない)
-
-
ログイン時には
/etc/profile/
というファイルと/etc/profile.d
というディレクトリ(の直下のファイル群)があり、それらが最初に読み込まれる。- 次に bash は
~/.bash_profile
->~/.bash_login
->~/.profile
の順番で探し、一番最初に見つかった時点で探索を終了し そのファイルを読み込む- なのでこれらに
.bashrc
を読み込む設定を書く必要がある。(デフォルトで書かれていることもある) -
~/.profile
に関してはbash
以外も使用するもの- なのでシェルの種類によらないスクリプトを記述する
- なのでこれらに
- あと
/etc/bash.bashrc
というファイルもある
- 次に bash は
よってまとめると以下のような順になる
diagrams:
login +-----------------+
| | must be read |
+--------------+ +-----------------+
| /etc/profile | +*****************+
+--------------+ | may not be read |
| +*****************+
+----------------+
| /etc/profile.d | <--- all `*.sh` files are loaded
+----------------+
| +***************+
if .bash_profile exists? ==Yes=> | .bash_profile | + + +
| No +***************+ +
| v
V +*************+ +
if .bash_login exists? ==Yes=> | .bash_login | + + + +
| No +*************+ +
| v
V +
+**********+ +
| .profile | <- + + + + may load + + + + + + + + + +
+**********+ +
+
+
v
+
+*********+ +
| .bashrc | <- + + + + may load + + + + + + + + + +
+*********+
---------------------------------------------------------
| |
| start interactive bash process (not a login shell) |
| | |
| +---------+ |
| | .bashrc | |
| +---------+ |
| |
---------------------------------------------------------
対話モード
とログインシェル
とは
man BASH(1) には以下のようにあります。
A login shell is one whose first character of argument zero is a -, or one started with the --login option.
An interactive shell is one started without non-option arguments (unless -s is specified) and without the -c option whose standard input and error are both connected to terminals (as determined by isatty(3)), or one started with the -i option. PS1 is set and $- includes i if bash is interactive, allowing a shell script or a startup file to test this state.
日本語版:
ログインシェル(login shell)とは、0 番目の引き数の最初の文字が - であるシェル、または --login オプション付きで起動されたシェルのことです。
対話的なシェルとは、 オプションでない引き数がなく、 標準入力と標準エラー出力がいずれも端末に接続されていて (これは isatty(3) で調べられます)、 -c オプションが指定されていない状態で起動されたシェル、または -iオプション付きで起動されたシェルのことです。 bash が対話的に動作している場合には、 PS1 が設定され、 $- に i が含まれます。 これを利用すると、対話的動作の状態であるかどうかを、 シェルスクリプトや起動ファイルの内部で調べられます。
ざっくりいうと
-
対話的
かつログインシェル
: ターミナルに入ったときやsu - <username>
でユーザにログインした時、ssh
でリモートの端末にログインした時など。 -
対話的
かつ非ログインシェル
:su <username>
のようにsu
コマンドに-
を付けずにログインした時や、ログイン後にユーザが明示的にbash
を実行した時など。- ただしこの場合でも
bash -l (or --login)
やexec -l bash
などとするとログインシェルとして起動する
- ただしこの場合でも
-
非対話
かつログインシェル
: 不明、そんなことあるのか? ただ、bash -l (or --login) <script-file>
で実現することはできた。 -
非対話
かつ非ログインシェル
: これらはスクリプトファイルを実行するときなどが当たる。
見分け方
-
ログインシェル
or非ログインシェル
:-
$ shopt login_shell
を実行し、login_shell on
と出ればログインシェル。login_shell off
とでれば非ログインシェル。ただしこれば bash 限定。 - またインタラクティブ状態で
echo $0
を実行し、-bash
のように先頭に-
があればログインシェル。ただしbash -l
などでログイン後に明示的にログインシェルとして起動したときは先頭に-
が付与されない(exec -l bash
の場合は付与される)。これはsu -
やexec -l
が bash の引数 0 つまり bash 自身に-
を付与するため。(exec
やsu
のman
参照)
-
-
対話
or非対話
:- プロンプト(
$PS1
) があれば大概は対話シェルである。 -
echo $-
で出力された文字列にi
があれば対話シェルである。
- プロンプト(
参考文献:
-
"bash の初期化ファイル .profile, .bashrc, .bash_profile の使い分けと管理方針 - A Memorandum".
https://blog1.mammb.com/entry/2019/12/01/090000 -
"105.1 レッスン 1 | LPI Learning".
https://learning.lpi.org/ja/learning-materials/102-500/105/105.1/105.1_01/ -
"30 シェルについての勉強 -- ログイン・シェルとインタラクティブ・シェル".
https://m-katsurada.sakura.ne.jp/knowhow-2023/node30.html -
"ログインシェル、インタラクティブシェルの見分け方(bash) - 朝から昼寝(こちらは旧URLです)".
https://happy-nap.hatenablog.com/entry/2022/12/04/112257 -
MAN BASH(1)
-
MAN SU(1)
設定ファイル類のベストプラクティス
複数の参考文献を見るに以下のような使い分けが筋が通っていて合意できるでしょう。
ファイル名 | 使い分け | 例 |
---|---|---|
~/.profile |
bash 以外でも使い、かつ対話シェル以外で使うものをかく。ウェルカムメッセージ(ログインメッセージ)を出力してもいい(/etc/profile.d に書くことが多い?) |
環境変数 等 |
~/.bash_profile |
~/.bashrc と /.profile を読みこむだけにする。(ここで読まないと両者は読まれない) |
なし |
~/.bash_login |
使わない | なし |
~/.bashrc |
対話シェルで使うもの。ここでは何も出力してはならないらしい。 |
エイリアス 自作コマンド $PS1 等 |
参考文献:
-
"Bash: .bashrcと.bash_profileの違いを今度こそ理解する|TechRacho by BPS株式会社".
https://techracho.bpsinc.jp/hachi8833/2021_07_08/66396 -
"bash - Difference between .bashrc and .bash_profile - Super User".
https://superuser.com/questions/183870/difference-between-bashrc-and-bash-profile -
"unix - Choosing between .bashrc, .profile, .bash_profile, etc - Super User".
https://superuser.com/questions/789448/choosing-between-bashrc-profile-bash-profile-etc
参考文献
-
"bash の初期化ファイル .profile, .bashrc, .bash_profile の使い分けと管理方針 - A Memorandum".
https://blog1.mammb.com/entry/2019/12/01/090000 -
"[Linux]環境変数の読み込み順番 #Bash - Qiita".
https://qiita.com/yunzeroin/items/480a3a677f78a57ac52f -
"Linuxで環境変数が保存される仕組み(bash_profile) #Bash - Qiita".
https://qiita.com/tomo0/items/674e06da84c921f5407a -
"105.1 レッスン 1 | LPI Learning".
https://learning.lpi.org/ja/learning-materials/102-500/105/105.1/105.1_01/ -
"30 シェルについての勉強 -- ログイン・シェルとインタラクティブ・シェル".
https://m-katsurada.sakura.ne.jp/knowhow-2023/node30.html -
"ログインシェル、インタラクティブシェルの見分け方(bash) - 朝から昼寝(こちらは旧URLです)".
https://happy-nap.hatenablog.com/entry/2022/12/04/112257 -
"ash(1) - Linux man page"
https://linux.die.net/man/1/ash -
"Bash: .bashrcと.bash_profileの違いを今度こそ理解する|TechRacho by BPS株式会社".
https://techracho.bpsinc.jp/hachi8833/2021_07_08/66396 -
"bash - Difference between .bashrc and .bash_profile - Super User".
https://superuser.com/questions/183870/difference-between-bashrc-and-bash-profile -
"unix - Choosing between .bashrc, .profile, .bash_profile, etc - Super User".
https://superuser.com/questions/789448/choosing-between-bashrc-profile-bash-profile-etc -
MAN BASH(1)
-
MAN SU(1)