0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

alpine linux / ash でも ashrc/bashrc(.*rc) が欲しい! (設定ファイル類の解説つき)

Posted at

Docker コンテナ等でよく使われる alpine linux. そのログインシェルである ash の設定ファイルについてです。

私自身 Docker 以外で alpine linux をよく使っています。(参考: iSH)
Docker を前提とした解決方法以外で、設定ファイルについて書かれた日本語の記事が(私が探した限りでは)なかったので記述します。

忙しい人向け

  • ash にはデフォルトで .bashrc に相当する対話シェル用の設定ファイルはありません
  • $HOME/.profile を作成し、その中に以下のものを記述することで、.bashrc 相当のファイル(以下だと .ashrc という名前)を作ることができます。(対話シェル用設定ファイルの名前は.ashrcである必要はなく、任意の名前が使えます。)
  • ただしこれだと対話シェル以外(シェルスクリプト実行時など)にも読み込まれるため、一工夫が必要です(後述)。
.profile
ENV=$HOME/.ashrc; export ENV

解説

結構探し回ったのですが、ash の man に答えがちゃんとありました。 (iSH にはそもそも man がデフォルトで存在しないので見逃してた)

ネット上以外で man ash(1) を手に入れることができなかったので、真偽は定かではありません。

ただし man busybox(1) は docker の alpine コンテナで手に入ったのでそれを見ようと思います。
と思ったら、 busybox(1) の man と以下の文言は全く別のものだった(どちらかと言ったら dash の方が近い)。 ただ ash --helpbusybox --help の出力が違うのは謎。readlink -f $(command -v ash)readlink -f $(command -v busybox) はどちらも /bin/busybox なのに ...

ですがとりあえず各所で根拠として挙げられている ash(1)man ページを示します。

ash(1)

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 で指定されたファイルに書きましょう(ただし実際には違う挙動をする(後述))

しかしこの章には続きがあります。

ash(1)

[省略] 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 と似ていると言えるのではないでしょうか。

man sh(1)

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 で確認しました。

.ashrc
DOT_ASHRC="$(date)"
export DOT_ASHRC
.profile
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 というファイルもある

よってまとめると以下のような順になる

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 自身に - を付与するため。(execsuman 参照)
  • 対話 or 非対話:
    • プロンプト($PS1) があれば大概は対話シェルである。
    • echo $- で出力された文字列に i があれば対話シェルである。

参考文献:

設定ファイル類のベストプラクティス

複数の参考文献を見るに以下のような使い分けが筋が通っていて合意できるでしょう。

ファイル名 使い分け
~/.profile bash 以外でも使い、かつ対話シェル以外で使うものをかく。ウェルカムメッセージ(ログインメッセージ)を出力してもいい(/etc/profile.d に書くことが多い?) 環境変数
~/.bash_profile ~/.bashrc/.profile を読みこむだけにする。(ここで読まないと両者は読まれない) なし
~/.bash_login 使わない なし
~/.bashrc 対話シェルで使うもの。ここでは何も出力してはならないらしい。 エイリアス 自作コマンド $PS1

参考文献:

参考文献

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?