
У меня возникли проблемы со случаем, который я рассматриваю как ошибку в работе оболочки или bash, при которой запуск неинтерактивной команды, предоставленной из heredoc (возможно, также из файла, я не проверял) и read
(или аналогичной) команды в heredoc, приводит к тому, что код после read
команды становитсяпроигнорировано, и исполнение заканчиваетсябез ошибок, выполняя командыпослекоманда, запускающая heredoc.
А точнее, я запускаю код в контейнере Docker, который служит набором инструментов для нескольких утилит Linux, и эта проблема делает код на самом деле небезопасным в том смысле, что я не могу быть уверен, что он будет работать так, как я ожидаю.
Пример проблемы можно увидеть ниже:
#!/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
инструкция, которая не выделяет псевдотерминал ( docker run -it
выделяет псевдотерминал, но не может выполнить приведенный выше код из-за heredoc, который передается в команду, вместо этого выдается ошибка: 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
команда была просто пропущена (например, с пустым значением в качестве входных данных) и все команды после нее были бы выполнены, даже те, что в heredoc, или чтобы она выдавала ошибку и немедленно останавливала выполнение (если я не игнорирую ошибку в команде 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
Я предполагаю, что команда read в конечном итоге считывает heredoc, как указал @muru в комментариях. Я также могу увидеть это, если добавлю echo "var=\$var"
после echo "after inside"
, что выведет: var=echo "after inside" >&2
. Так что теперь мне нужно найти способ пропустить эти чтения из самого heredoc.