为什么docker run命令会终止外层bash脚本的循环执行?
问题原因与排查解决方法
核心根因
你遇到的循环提前退出问题,90%以上的场景是docker run命令消费了while循环的标准输入流,导致第二次循环的read命令拿到EOF直接终止循环。
排查步骤
- 第一步先验证循环本身是否正常:把代码中docker run的整段代码替换为
echo "processing endpoint: ${i}",执行脚本看是否能输出所有的endpoint值。如果可以完整输出,说明问题完全出在docker run逻辑上,排除jq解析、read读取的问题。 - 第二步检查脚本是否开启了错误退出规则:如果脚本开头有
set -e/set -o errexit配置,只要docker run返回非零退出码,会直接终止整个父脚本。你可以在脚本开头加set -x开启调试日志,确认脚本退出前最后一步的返回值是否异常。 - 第三步验证detached模式的配置是否正确:如果你使用
-d参数启动容器,建议先去掉--rm参数,容器运行后用docker ps -a查看所有容器的运行状态、退出码,确认是否是容器自身运行出错连带父脚本退出。
可行解决方案
方案1(最简单,适用于无需给容器传标准输入的场景)
直接删除docker run命令中的-i和-a stdin参数,这两个参数会让容器绑定父进程的标准输入流,主动读取输入内容,把jq输出的剩余endpoint值全部消费掉,导致后续read无数据可读。修改后的docker run示例:
docker run -a stdout -a stderr --rm \ -v "$PWD":/tmp \ -w /tmp \ -e SLICE \ -e STAGE \ -e PREFIX \ -e AWS_REGION \ -e NAME \ -e BUCKET \ $CONTAINER --verbose $TEMPLATE_PATH
方案2(需要保留交互模式的场景)
给docker run命令的标准输入重定向到/dev/null,避免它读取循环的输入流,修改示例:
docker run -i -a stdin -a stdout -a stderr --rm \ -v "$PWD":/tmp \ -w /tmp \ -e SLICE \ -e STAGE \ -e PREFIX \ -e AWS_REGION \ -e NAME \ -e BUCKET \ $CONTAINER --verbose $TEMPLATE_PATH < /dev/null
方案3(从循环写法上规避标准输入冲突)
不用管道传递jq输出,改用进程替换把所有endpoint读入数组后遍历,完全避免标准输入共用的问题:
# 先把所有endpoint存入数组 mapfile -t endpoints < <(jq -c -r '.triggers.endpoint[]' config.json) # 遍历数组执行逻辑 for i in "${endpoints[@]}" do echo "Running container for ${i}" export TEMPLATE_PATH=$1 export BUCKET="something" export UPPERCASE_ENDPOINT=${i^^} export NAME="${APPLICATION}_${UPPERCASE_ENDPOINT}" export PREFIX="something/${i}" docker run -i -a stdin -a stdout -a stderr --rm \ -v "$PWD":/tmp \ -w /tmp \ -e SLICE \ -e STAGE \ -e PREFIX \ -e AWS_REGION \ -e NAME \ -e BUCKET \ $CONTAINER --verbose $TEMPLATE_PATH done
方案4(规避容器错误导致父脚本退出)
如果是docker run非零退出码触发的脚本终止,可以给docker run增加错误处理逻辑,避免直接退出:
if ! docker run ...... # 这里替换为你的docker run参数 then echo "container for ${i} run failed" # 可以在这里加自定义错误处理逻辑 fi
内容的提问来源于stack exchange,提问作者iammr10k
相关产品推荐
相关产品推荐

