以下のように、変数への値がセットされているかで分岐するシェルスクリプトがあったとします。このスクリプトは正常に動作します。
#!/bin/sh
if [ -n "$CIRCLE_PULL_REQUEST" ]; then
echo '$CIRCLE_PULL_REQUEST is defined.'
else
echo '$CIRCLE_PULL_REQUEST is NOT defined.'
fi
堅牢なシェルスクリプトを書く時は #!/bin/sh
を #!/bin/sh -u
にすることが多いです。typo 等による意図しない変数の利用を防いでくれます。
今回はこれがコンフリクトし、以下のようなエラーになります。
$ ./v.sh
./v.sh: line 3: CIRCLE_PULL_REQUEST: unbound variable
対策方法
set -uしてるときに変数が定義されてるかチェックする - Qiita
残念ながら macOS High Sierra では上記の方法は使えませんでした。
$ ./v.bash
./v.bash: line 3: conditional binary operator expected
$ which -a bash
/bin/bash
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
v.bash の中身です。
#!/bin/bash -eu
if [[ -v CIRCLE_PULL_REQUEST ]]; then
echo 'CIRCLE_PULL_REQUEST is defined.'
else
echo 'CIRCLE_PULL_REQUEST is NOT defined.'
fi
手元の Linux では使えました。Bash は v4.3.11 でした。
$ ./v.bash
CIRCLE_PULL_REQUEST is NOT defined.
$ CIRCLE_PULL_REQUEST=123 ./v.bash
CIRCLE_PULL_REQUEST is defined.
$ bash --version
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 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.
Bourne Shell から使えるデフォルト値記述なら、Bash v3 系でも希望の動作になりました。
#!/bin/sh -eu
if [ "${CIRCLE_PULL_REQUEST:-unknown}" != unknown ]; then
echo 'CIRCLE_PULL_REQUEST is defined.'
else
echo 'CIRCLE_PULL_REQUEST is NOT defined.'
fi
CIRCLE_PULL_REQUEST が未定義なら、左辺は “unknown” になります。この変数には整数の文字列しか入らないという前提に依存しています。つらい。
結果。
$ ./d.sh
CIRCLE_PULL_REQUEST is NOT defined.
$ CIRCLE_PULL_REQUEST=123 ./d.sh
CIRCLE_PULL_REQUEST is defined.
シェバンを /bin/sh に出来たので、自己満足にはなりました。
by なるべく Bourne Shell 記法で書きたい奴
※ macOS High Sierra では sh の実体は bash です。
$ which -a sh
/bin/sh
$ ls -alF /bin/sh
-r-xr-xr-x 1 root wheel 618512 Oct 26 2017 /bin/sh*
$ sh --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
蛇足
-e も合わせて使うことが多いです。→ ‘#!/bin/sh -eu’
シェルスクリプトの途中で exit code が 0 以外のプロセスがあれば、即座に停止してくれます。
さらに蛇足ですが、シェバンにセットすると処理系によっては解釈されないらしい(Windows?)ので、
#!/bin/sh
set -eu
などと、set を使うとポータビリティの高いシェルスクリプトになります。面倒なので私はシェバンに書いてしまいますが…。
追記(2018-10-22):
前提が説明不足だったので、全体的に補完しました。