讀取指令跳過 HEREDOC 中的後續指令 - Bash + Docker

讀取指令跳過 HEREDOC 中的後續指令 - Bash + Docker

我遇到了一個問題,我認為 shell 或 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在命令中有一條指令不分配偽tty(docker run -it分配偽tty,但無法運行上面的程式碼,因為透過管道傳輸到命令的heredoc,而是給出了錯誤:the input device is not a TTY)。

如果我刪除 2set -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 varecho "after inside"輸出:

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

我猜想 read 命令最終會從定界文檔中讀取,正如@muru 在評論中指出的那樣。如果我添加echo "var=\$var"after echo "after inside",它會打印:var=echo "after inside" >&2,我也可以看到它。所以現在我必須找到一種方法來跳過來自定界文檔本身的那些讀取。

相關內容