Shell Script 上の for における in の中は一度にすべて評価される


背景

以下のようなスクリプトを実行したときに、CMD2 以降はいつ評価されるのかが気になった。

for a in "$(CMD1)" "$(CMD2)" "$(CMD3)" ...; do
  :
  [ $a = 'foo' ] && break
done

仮に一度にすべて評価されるとするならば、特に CMD たちが AWS CLI だったような場合、必要以上にレスポンスに時間がかかったり、クオータ制限に達する可能性がある。CLI の実行は最小限におさえたい。

環境

WSL2 + Ubuntu (dash)

$ dpkg -s dash | grep Version
Version: 0.5.8-2.10

検証

以下スクリプトを実行。

for a in "$(echo hello | tee /tmp/hello)" "$(echo hello2 | tee /tmp/hello2)"; do
    [ $a = 'hello' ] && echo 'broke' && break
done

broke と表示される前提で、 /tmp 配下に hello2 ファイルがあるかどうかを調べる。
もし hello2 ファイルが

  • ないならば: 当該ループに入るタイミングで初めて評価される
  • あるならば: 初回ループ実行前にすべて評価される

ということがいえる。

結果

$ for a in "$(echo hello | tee /tmp/hello)" "$(echo hello2 | tee /tmp/hello2)"; do
>   [ $a = 'hello' ] && echo 'broke' && break
> done
broke

$ ls /tmp/hello*
/tmp/hello  /tmp/hello2

hello2 ファイルが存在した。つまり、ループ開始前に hello および hello2 が評価されていることがわかった。