HEREDOC で後続の命令をスキップする読み取りコマンド - Bash + Docker

HEREDOC で後続の命令をスキップする読み取りコマンド - Bash + Docker

シェルやbashの動作にバグがあると思われるケースで困っています。ヒアドキュメント(ファイルからかもしれないが、テストはしていない)から提供された非対話型コマンドとヒアドキュメント内の(または同様の)コマンドを実行すると、コマンドreadの後のコードがread無視された、そして処刑は終了するエラーなし、コマンドを実行するヒアドキュメントを実行するコマンド。

具体的には、いくつかの Linux ユーティリティのツールボックスとして機能する Docker コンテナでコードを実行していますが、この問題により、期待どおりに実行されるかどうか信頼できないという意味で、コードが実際には安全ではなくなります。

問題の例を以下に示します。

#!/bin/bash
set -eou pipefail

echo "before outside" >&2

docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
    set -eou pipefail
    echo "before inside" >&2
    read 'var'
    echo "after inside" >&2
SHELL

echo "after outside" >&2

上記のコードは、すべてのエコーを実行して正常に終了するか、コマンドでエラーが発生しreadてエラーで終了すると予想されます。

残念ながら、上記のコードの出力は次のようになります。

before outside
before inside
after outside

read基本的にコマンドの後に続くものは無視されますreadおそらく、疑似 tty を割り当てないコマンドの命令があるためです(docker run -it疑似 tty を割り当てますが、コマンドにパイプされているヒアドキュメントのために上記のコードを実行できず、代わりにエラーが発生します: the input device is not a TTY)。

2つを削除してset -eou pipefailも同じ問題が発生するのでread 'var' ||:、(落とし穴の)set -e

期待される結果の例は次のとおりです。

#!/bin/bash
set -eou pipefail

echo "before outside" >&2

docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
    set -eou pipefail
    echo "before inside" >&2
    unknow_command error ||:
    echo "after inside" >&2
SHELL

echo "after outside" >&2

これは正常に終了し、次のように出力されます:

before outside
before inside
/bin/bash: line 3: unknow_command: command not found
after inside
after outside

または:

#!/bin/bash
set -eou pipefail

echo "before outside" >&2

docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
    set -eou pipefail
    echo "before inside" >&2
    unknow_command error
    echo "after inside" >&2
SHELL

echo "after outside" >&2

これはエラーで終了し、次のように出力されます:

before outside
before inside
/bin/bash: line 3: unknow_command: command not found

私の場合、readコマンドがスキップされ(たとえば、入力として空の値を使用)、ヒアドキュメント内のコマンドも含め、その後のすべてのコマンドが実行されるか、エラーが発生して実行が直ちに停止される(docker コマンドのエラーを無視しない場合)のであれば問題ありません。

上記は単なる例です。実際のケースでは、read(または同様の) コマンドが直接呼び出されず、コマンド内で、特定の条件下でのみ呼び出される可能性があるため、状況はさらに悪くなる可能性があります。例:

#!/bin/bash
set -eou pipefail

docker run --rm -i my_mysql /bin/bash <<-SHELL
    set -eou pipefail
    some_important_command_1
    mysql -u "$user" -p "$pass" -e "some sql command"
    some_important_command_2
SHELL

some_important_command_after_2

上記のコードは問題ないように見えるかもしれませんが、パスワードが空の場合、stdin から読み取ろうとするため、最初の例で問題が発生し、 がスキップされますsome_important_command_2が、some_important_command_after_2の実行は の後にのみ実行される必要がありますsome_important_command_2

上記の mysql の例も単なる例ですが、この場合はパスワードが空かどうかを確認して処理することができます。本当の問題は、この問題がコード内で発生するかどうか判断できず、それを回避する安全な方法が見つからないことです。、docker toolbox コンテナの使用を停止し、すべてのユーティリティとその他のものをすべてのホスト内にインストールし、それらを最新の状態に保つこと(コンテナ イメージを最新の状態に保つだけでなく)を除きます。また、コンテナ内のサービスで具体的に実行されるコマンド(上記の mysql の例など)では機能しません。

上記の問題に対する解決策を持っている人はいますか?解決策は具体的ではない私が挙げた例とは異なり、一般的なアプローチで解決できるこのタイプのバグ(正常に完了するか、すべてのコマンドを実行するか、エラーを出して後続のすべてのコマンドを停止します)。

アップデート:

declare -p var出力後の追加echo "after inside":

before outside
before inside
declare -- var="echo \"after inside\" >&2"
after outside

@muru がコメントで指摘したように、read コマンドは最終的に heredoc から読み取ることになると思います。echo "var=\$var"after を追加してecho "after inside"、次のように出力した場合にも、同じことがわかります。var=echo "after inside" >&2したがって、heredoc 自体からの読み取りをスキップする方法を見つける必要があります。

関連情報