SHELL 指令是什么
SHELL 用来改变后续 shell 形式指令所使用的解释器。Linux 容器默认是 sh 加 -c 的组合,因此在 RUN 里写管道与逻辑运算符都走 sh 的语义;如果脚本依赖 bash 特性(如 pipefail、数组、命令替换细节),就要先切到 bash。Windows 容器默认用 cmd 形式,需要 PowerShell 时同样靠这条指令切换。
基本信息
| 语法 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] |
|---|---|
| 默认值 | Linux 容器为 ["/bin/sh", "-c"];Windows 容器为 cmd 形式 |
| 影响范围 | 后续所有 shell 形式的 RUN/CMD/ENTRYPOINT |
| 生效期限 | 从该指令之后开始,直到文件结束(不作用于已写过的指令) |
| 前提条件 | 目标解释器必须存在于镜像中 |
| 相关指令 | exec 形式的 JSON 数组指令不受 SHELL 影响 |
典型使用场景
- 脚本里需要 pipefail:管道中任一环节失败即整体失败
- 使用 bash 专有语法(数组、[[ ]] 判断、进程替换)
- Windows 容器切换到 PowerShell 执行脚本
- 统一多阶段构建中各阶段的脚本行为,避免 sh 与 bash 混用
- 排查脚本误报成功:默认 sh 下管道失败不会导致整体失败
切换与使用
切换与使用
# 切到 bash 并开启管道错误传播:任一环节失败即整体失败
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN set -euo pipefail && ./scripts/build.sh | tee build.log
# Windows 容器切到 PowerShell
# SHELL ["powershell", "-Command"]
# 注意:exec 形式不受 SHELL 影响
# RUN ["bash", "-lc", "set -euo pipefail && ./build.sh"] 会直接调用 bash验证脚本行为
验证脚本行为
# 在镜像内确认默认 shell 是什么
docker run --rm -it --entrypoint /bin/sh alpine -c 'echo $0; ls -l /bin/sh'
# 确认 bash 是否可用(alpine 需先安装)
docker run --rm alpine /bin/bash -c 'echo ok' || echo 'bash 不存在,需要安装'
# 启用 pipefail 前后对比:默认 sh 下失败被吞掉的现象
# docker run --rm alpine sh -c 'false | true; echo $?' # 输出 0,问题被掩盖
# docker run --rm bash:5 bash -o pipefail -c 'false | true; echo $?' # 输出 1关键差别
| 方面 | 默认 sh | 切换 bash -o pipefail |
|---|---|---|
| 管道失败 | 只反映最后一个命令 | 任一环节失败即失败 |
| 语法支持 | 基础 POSIX | 支持数组与扩展判断 |
| 脚本报错 | 可能静默通过 | 失败更早暴露 |
| 镜像依赖 | 无需额外安装 | 镜像必须提供 bash |
| exec 形式 | 不受影响 | 不受影响 |
切换到 bash 的前提是镜像里装了 bash:alpine 等精简镜像默认只有 sh,需要先安装再切换,否则该条 RUN 会直接失败。
SHELL 只影响 shell 形式的指令。用 JSON 数组写的 RUN 会直接执行指定程序,不会经过 SHELL 声明的解释器。