百日半狂乱

Shut the fuck up and write some code!!

シェル変数のデフォルト値を設定する、未初期化時にエラーメッセージを出力してスクリプトを強制終了する

二十五日半狂乱4日目(の分)の記事

前回引用したkilltreeスクリプトの中に以下のようなコードがあった.

local _sig=${2:-TERM}

この${2:-TERM}は、変数展開されたタイミングで、$2に値が設定されていない場合にTERMを出力する.

すなわち、結果的に$2が空だった場合は変数_sigにTERMが代入される.

なのでkilltreeは第二引数でシグナルを指定せずに実行したらデフォルトでkill -TERM [pid]が実行される.

$ bash killtree.bash 4003
kill -TERM 4004
kill -TERM 4005
kill -TERM 4006
kill -TERM 4007
kill -TERM 4008
kill -TERM 4003

[2]+  Stopped                 bash mkpstree.bash

手元にあるUNIXシェルスクリプトコマンドブックを見てみると、同書ではこの機能を「拡張的な変数展開」と呼んでいて、他にも便利な機能が色々解説されていた.

UNIXシェルスクリプトコマンドブック 第2版

UNIXシェルスクリプトコマンドブック 第2版

買った技術書の後半を読み切らずに本棚へしまっていたことが白日の下に晒されつつ、便利な変数展開をメモ.

シェル変数のデフォルト値を設定する

冒頭の変数展開は、変数に値が設定されていなければ、代わりに指定した値を返す.

$ echo ${var:-hoge}
hoge
$ var=foo
$ echo ${var:-hoge}
foo
$ var=
$ echo ${var:-hoge}
hoge
$ echo $var
#varにhogeが代入されるわけではない

foo=${var:-hoge}のようにvarとは別のシェル変数にデフォルト値を設定したい場合ならこれで事足りるが、var自身にデフォルト値を設定したい場合は${var:=word}が使える.

シェル変数のデフォルト値を設定する

3行目はkilltreeと同じ用途のサンプル.それはさておき.

目的は変数varへのデフォルト値設定なので、変数展開だけを行えば目的は達成できるが、7行目のような感じで書いても所詮変数なので上手くいかない. 11行目のように変数展開を行った上で出力を捨てる方法もあるが、スマートじゃない. これに対して同書では15行目や20行目のコマンド:で変数展開だけを行うテクニックが紹介されていた.

コマンド:は何もせずに終了コード0を返すだけコマンドだが、引数を取る場合は、引数が拡張的な変数展開の場合にそれをチェックして変数展開を行ってくれる.

これでももう: ${var:=default_value}とするだけで、

if [ "$var" = "" ]; then
var=default_value
fi

などと煩わしいこと*1をしなくて済む.

無知って怖い.

 未初期化時にメッセージを出力してスクリプトを終了する

ずっとシェルスクリプトで変数未初期化の検知は、やりにくい上に面倒だ、デバッグも泥臭くてしんどい、などと思っていたがスマートな方法があった.

varに値がセットされていない場合、以下のような感じでエラーメッセージを出力できる*2

$ : ${var:?"error message"}
bash: var: error message

このコードがスクリプト上にあった場合には、その時点でシェルを終了して上記のようなエラーを出力してくれる.

変数未定義が致命的なエラーとなるスクリプトの場合には、検知した時点でスクリプトの処理を中断して適切なエラーメッセージを吐き出すことができる.

また、このメッセージ出力は標準出力でも標準エラー出力でもなく、メッセージをリダイレクトすることも、変数に代入することもできないので、常に強制的に処理を中断させることができる.

$ echo ${var:?"error message"} > /dev/null 2>&1
bash: var: error message

同書には、他にも:を省略した場合の挙動や、bashで追加された拡張的な変数展開なども紹介されている.

*1:この初期化方法は煩わしい上に、if文で変数に対してクォーティングをすべきかなどの不要な不安も付き纏う

*2:bashが変数名も一緒に出力してくれる!