Bourne/Ash シェルで変数・環境変数が未定義・空かを確認・判定する【Alpine Linux など】


変数の未定義チェックに Bash シェルの -v オプションを Bourne シェルで使うと、unknown operand エラーが発生する。

Alpine Linux などの Bourne 系シェル(Ash)で変数や環境変数の未定義をチェックできないか

bourne シェル 未定義 確認」とググっても、Bash シェルの情報ばかりだったので、自分のググラビリティとして。

TL;DR

シェルの変数展開(${})で、代替値(:+)を使って検知する

未定義or空文字なら1(false)、定義済み&値入りなら0(true)
# Bourne/Ash/Bash/Dash コンパチ(互換のある)構文
[ "${VAR:+defined}" ] || {
    echo '変数 VAR が未定義もしくは空です。'
    exit 1
}

TS;DR

確かに Alpine Linux で bash を使いたいならインストールすればいいだけなのです。しかし、Docker での利用なので、なるべく余計なものを入れたくない(レイヤーを作りたくない)のです。

Docker コンテナ起動時にホストからコンテナに環境変数を渡す必要がある場合、必須の環境変数がセットされていないとエラーでコンテナが起動しないようにしたいのです。

「未定義ならエラーで終了」だけならコメントいただいたように set -u をスクリプトなどに記載するのが簡単です。ポカヨケをしたい場合は、関連するスクリプトファイルに : ${VAR?} を記載しておくのもいいかもしれません。

任意のエラーメッセージ

ただ、今回は「未定義の場合もしくは必須項目が空の場合は任意のメッセージを表示して終了」させたいのです。

任意のメッセージとは「どのファイルに環境変数を記載すべきか」などのユーザーへの指示メッセージです。そのため、判定式が必要だったのです。

下記は、変数定義の判定に -v もしくは -z オプションを使った方法と set コマンドを使った方法を説明しています。

おすすめはコメントにいただいた set コマンドを使った方法で、シンプルで可読性の高い書き方だと思います。

検証環境情報

Alpine3.10.2
~ # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.10.2
PRETTY_NAME="Alpine Linux v3.10"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
shのバージョン(Bourneというより正確にはAsh)
~ # sh --help
BusyBox v1.30.1 (2019-06-12 17:51:55 UTC) multi-call binary.

Usage: sh [-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]

Unix shell interpreter
bashのバージョン(検証用に別途インストール)
~ # bash --version
GNU bash, version 5.0.0(1)-release (x86_64-alpine-linux-musl)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

-v オプションの場合

ググると、変数の定義確認で一番出てくる方法です。問題は Bourne/Ash シェルでは使えないことです。

sample1.sh
~ # cat sample1.sh
#!/bin/sh

[ -v HOGE ] || {
    echo 'Not defined HOGE'
    exit 1
}

echo 'HOGE is defined with value:' $HOGE
環境変数を未定義でテスト
~ # unset HOGE

~ # # Bash シェルで実行(正常)
~ # bash sample1.sh
Not defined HOGE

~ # # Bourne シェルで実行(エラー)
~ # sh sample1.sh
sh: HOGE: unknown operand
Not defined HOGE
環境変数を定義してテスト
~ # export HOGE=FUGA

~ # # Bash シェルで実行(正常)
~ # bash sample1.sh
HOGE is defined with value: FUGA

~ # # Bourne シェルで実行(エラー)
~ # sh sample1.sh
sh: HOGE: unknown operand
Not defined HOGE
空の環境変数を定義してテスト
~ # export HOGE=''

~ # # Bash シェルで実行(正常)
~ # bash sample1.sh
HOGE is defined with value:

~ # # Bourne シェルで実行(エラー)
~ # sh sample1.sh
sh: HOGE: unknown operand
Not defined HOGE

-z オプションの場合

-v オプションが使えない場合に -z オプションを使った bash/bourne/ash 互換の代替法です。定義されているか否かの判定だけで、値が空の場合は真(true)になっていることに注意ください。

sample2.sh
~ # cat sample2.sh
#!/bin/sh

[ -z "$HOGE" ] && [ "${HOGE:-A}" = "${HOGE-A}" ] && {
    echo 'Not defined HOGE'
    exit 1
}

echo 'HOGE is defined with value:' $HOGE
どちらも同じように動いた
~ # unset HOGE
~ # bash sample2.sh
Not defined HOGE
~ # sh sample2.sh
Not defined HOGE

~ # export HOGE=''
~ # bash sample2.sh
HOGE is defined with value:
~ # sh sample2.sh
HOGE is defined with value:

~ # export HOGE=FUGA
~ # bash sample2.sh
HOGE is defined with value: FUGA
~ # sh sample2.sh
HOGE is defined with value: FUGA

~ # # 🎉

シェルの変数展開で代替値を使った場合

この方法は、変数が定義済みだけでなく値が空の場合も検知できるので、必須の環境変数をチェックする際に有用だと思います。

シェルの変数展開、つまり echo "The parameter is: ${parameter}" のように ${} で変数を展開する際に :+ を使うアイデアです。

${parameter:+word}:+ を使うと ${parameter}NULL 以外の場合 word に置換されます。

sample3.sh
~ # cat sample3.sh
#!/bin/sh

[ "${HOGE:+dummy}" ] || {
    echo 'Not defined HOGE or empty value'
    exit 1
}

echo 'HOGE is defined with value:' $HOGE
どちらも同じように動いた
~ # unset HOGE
~ # bash sample3.sh
Not defined HOGE or empty value
~ # sh sample3.sh
Not defined HOGE or empty value

~ # export HOGE=''
~ # bash sample3.sh
Not defined HOGE or empty value
~ # sh sample3.sh
Not defined HOGE or empty value

~ # export HOGE=FUGA
~ # bash sample3.sh
HOGE is defined with value: FUGA
~ # sh sample3.sh
HOGE is defined with value: FUGA

~ # # 🎉

参考文献