職業プログラマの休日出勤

職業プログラマによる日曜自宅プログラミングや思考実験の成果たち。リアル休日出勤が発生すると更新が滞りがちになる。記事の内容は個人の意見であり、所属している(いた)組織の意見ではない。

bashでheredoc内容を変数に入れたら落ちる

表題のことで少しハマったので、その反省文です。

tweet

目的

bashスクリプトファイル内で、heredoc記法で書かれた複数行の文字列(中では変数展開あり)を、bashの変数に格納して使いたい。

落ちるスクリプトと、その実行結果

#!/bin/bash
set -e
set -u
set -x

TODAY=$(date +%Y-%m-%d)
read -r -d '' MESSAGE <<EOF
今日は ${TODAY} です。
これは2行目のテキストです。
EOF

echo "done"
echo "$MESSAGE"

このスクリプトを保存して動かすと、以下のような出力があり、終了ステータス1で終了します。エラーメッセージは特に何もありません。

++ date +%Y-%m-%d
+ TODAY=2023-03-14
+ read -r -d '' MESSAGE

これらの出力は、 set -x による効果で、実行しているコマンドのログです。
このログから、readコマンドは実行されているのに、最後の2つのechoコマンドは実行されていないことがわかります。

なぜ落ちるのか?

readコマンドは、EOFにより読み込みを終了した場合は、exit status が0ではありません。
linuxcommand.org
手元の環境では1だったのですが、 set -e オプションが指定されている状況においては、これはスクリプトを終了させる原因になります。

落ちないスクリプト

readコマンドの実行時だけ、エラー時即時終了オプション set -e を解除すれば、とりあえずの解決になります。

#!/bin/bash
set -e
set -u
set -x

TODAY=$(date +%Y-%m-%d)
set +e
read -r -d '' MESSAGE <<EOF
今日は ${TODAY} です。
これは2行目のテキストです。
EOF
set -e

echo "done"
echo "$MESSAGE"

set -e のご加護が無い時の注意

エラー時即時終了というのは、多くの場合でとても有益であり、間違いに素早く気付くキッカケになります。このご加護をoffにするということは、エラーに気付きにくくなるということです。
heredocを書いている途中でよくあるエラーと言えば、展開する変数のスペルを間違うことだと思いますが、これは set -u が効いて、きちんと落ちてくれます。
他のエラーの例がぱっと思いつかなかったのですが、この解決策を適用する際は「-eのご加護が無い」ということを肝に銘じておきましょう。

どうやってこの原因究明を成し遂げたのか?

AskUbuntuの回答についたコメントを見ることで気付きました。自力では解決できませんでした(反省)
stackoverflow.com

環境情報

以下のbashで動作確認しました。

バージョン番号が雑でスマン…