Read command skip subsequent instructions in HEREDOC - Bash + Docker

Read command skip subsequent instructions in HEREDOC - Bash + Docker

Ich habe Probleme mit einem Fall, den ich als Fehler in der Funktionsweise der Shell oder Bash ansehe. Das Ausführen eines nicht interaktiven Befehls aus einem Heredoc (vielleicht auch aus einer Datei, das habe ich nicht getestet) und eines read(oder ähnlichen) Befehls im Heredoc führt dazu, dass der Code nach einem readBefehlignoriertund die Ausführung endetohne Fehler, Ausführen der Befehlenachder Befehl, der das Heredoc ausführt.

Genauer gesagt führe ich Code in einem Docker-Container aus, der als Toolbox für verschiedene Linux-Dienstprogramme fungiert, und dieses Problem führt dazu, dass der Code insofern nicht wirklich sicher ist, als dass ich nicht darauf vertrauen kann, dass alles wie erwartet funktioniert.

Unten sehen Sie ein Beispiel des Problems:

#!/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

Ich würde erwarten, dass der obige Code entweder alle Echos ausführt und erfolgreich endet oder einen Fehler im readBefehl ausgibt und mit einem Fehler endet.

Leider lautet die Ausgabe des obigen Codes:

before outside
before inside
after outside

Es ignorierte grundsätzlich, was nach dem readBefehl kam, möglicherweise weil ein Befehl eine Anweisung enthält read, die kein Pseudo-TTY zuweist ( docker run -itweist ein Pseudo-TTY zu, kann den obigen Code jedoch aufgrund des Heredoc, das an den Befehl weitergeleitet wird, nicht ausführen, sondern gibt stattdessen den Fehler aus: the input device is not a TTY).

Wenn ich die 2 entferne, set -eou pipefailhabe ich das gleiche Problem, sogar mit read 'var' ||:, also glaube ich nicht, dass es damit zusammenhängt (eines derFallstrickevon) set -e.

Ein Beispiel für ein erwartetes Ergebnis ist wie folgt:

#!/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

Das endet erfolgreich und druckt:

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

Oder:

#!/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

Das endet mit einem Fehler und druckt:

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

Für mich wäre es ok, wenn der readBefehl einfach übersprungen würde (zum Beispiel mit einem leeren Wert als Eingabe) und alle nachfolgenden Befehle ausgeführt würden, auch die im Heredoc, oder dass ein Fehler ausgegeben und die Ausführung sofort gestoppt würde (wenn ich den Fehler im Docker-Befehl nicht ignoriere).

Das obige war nur ein Beispiel. In der Praxis kann es noch schlimmer sein, da der read(oder ein ähnlicher) Befehl nicht direkt aufgerufen werden kann, sondern innerhalb eines Befehls und nur unter bestimmten Bedingungen. Beispiel:

#!/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

Der obige Code scheint in Ordnung zu sein, aber wenn das Kennwort leer ist, wird versucht, von stdin zu lesen, wodurch das Problem im ersten Beispiel auftritt und übersprungen wird. Die some_important_command_2Ausführung some_important_command_after_2sollte jedoch erst nach erfolgen some_important_command_2.

Das obige MySQL-Beispiel war auch nur ein Beispiel. Ich kann in diesem Fall überprüfen, ob das Passwort leer ist, und es verarbeiten.Das eigentliche Problem ist, dass ich nicht feststellen kann, ob dieses Problem innerhalb eines Codes auftreten kann, und ich sehe keinen sicheren Weg, es zu vermeiden, abgesehen davon, dass Sie die Verwendung des Docker-Toolbox-Containers beenden und alle Dienstprogramme und andere Dinge in allen Hosts installieren und auf dem neuesten Stand halten (anstatt nur das Container-Image auf dem neuesten Stand zu halten). Es funktioniert auch nicht für Befehle, die speziell in Diensten innerhalb von Containern ausgeführt werden (wie das MySQL-Beispiel oben).

Hat jemand eine Lösung für das obige Problem?Eine Lösung, dieist nicht spezifisch to the examples I gave, but a general approach that can solve this type of bug (either by completing successfully, executing all commands, or giving an error and stopping all subsequent commands).

Update:

Adding declare -p var after echo "after inside" outputs:

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

I guess that the read command ends up reading from the heredoc as @muru pointed in the comments. I can also see it if I add echo "var=\$var" after echo "after inside", which prints: var=echo "after inside" >&2. So now I have to see a way to skip those reads from the heredoc itself.

verwandte Informationen