快速修复
# 一键:非交互执行,去掉 -t
docker exec <容器> sh -c "你的命令"
你会遇到什么
脚本、CI(Jenkins/GitHub Actions)或管道里跑 docker exec -it ... 报 the input device is not a TTY;同族还有 container is not running、exec: "bash": executable file not found in $PATH。手动在终端里跑同一命令却正常——所以总被当成玄学。
其实规律很简单:交互式命令(-t)在非终端环境里跑不了。
本文路径:快速修复(30 秒一段命令)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。
为什么会这样
-t(--tty)要求分配 TTY 伪终端,脚本/CI 的标准输入不是终端,docker CLI 直接拒绝启动;docker compose exec默认带-t,所以 compose 场景更容易踩;container is not running:对已停止容器执行 exec;executable file not found:alpine 等精简镜像没有bash(只有sh)。
实测环境:WSL Ubuntu,docker 29.1.3,alpine:3.20,2026-08-29。
-t 的语义是「让容器进程认为自己在终端里」:docker CLI 会为它分配一个伪终端(PTY)。PTY 只能建立在「客户端 stdin 是终端」的前提下;脚本/CI 里 stdin 是管道或文件,没有终端可分配,CLI 在启动前就拒绝。docker compose exec 默认带 -t,所以 compose 场景更容易踩。
确定性推论:报 the input device is not a TTY = 当前进程没有终端,去掉 -t(需要 stdin 时只留 -i);脚本里用 [ -t 0 ] 检测 stdin 是否终端来决定要不要 -t,不要拍脑袋写死。
一步步来
1. 非交互命令去掉 -t — 必做
docker exec <c> sh -c "命令"
需要 stdin 管道时只留 -i:
echo 数据 | docker exec -i <c> sh -c "cat"
(实测均可,exit 0。)
2. compose 场景 — 必做
docker compose exec -T <service> 命令
-T = 禁用 TTY。旧版 v1 可加 export COMPOSE_INTERACTIVE_NO_CLI=1,新版以 -T 为准。
3. 容器没在跑 — 必做
docker start <c>
先 docker logs <c> 看为什么退出(exit code 1 的容器先排查启动失败原因,别盲目 start)。
4. 镜像里没有目标命令 — 必做
docker exec <c> ls /bin /usr/bin
alpine 用 sh;要 bash 就换 ubuntu/debian 镜像,或 docker exec <c> sh -c "命令"。
真终端里交互式进入容器保持原样:docker exec -it <c> sh,不要去掉-it。
怎么确认修好了
docker exec <c> sh -c "echo noninteractive-ok"
正常输出即成功。实测:echo hello | docker exec -i <c> sh -c "cat" → hello(exit 0);复现时 docker exec -it <c> sh </dev/null → the input device is not a TTY(exit 1)。
其它环境的表现
| 环境 | 说明 |
|---|---|
| 脚本/CI/管道 | 去掉 -t,只留 -i(需要 stdin 时) |
| docker compose | exec -T 禁用 TTY |
| 已停止容器 | container is not running,先 docker start / 查日志 |
| alpine 精简镜像 | 没有 bash,用 sh -c |
症状
脚本、CI(Jenkins/GitHub Actions)或管道里跑 docker exec -it ... / docker compose exec ... 报:
the input device is not a TTY
同族症状:docker exec 进已停止容器报 Error response from daemon: container <id> is not running; 对 alpine 等精简镜像跑 docker exec <c> bash 报 exec: "bash": executable file not found in $PATH。 特征:交互式命令在没有终端的环境里执行;手动在终端里跑同一命令却正常——所以总被当成玄学。
原因
-t(--tty)要求分配一个 TTY 伪终端,脚本/CI 的标准输入不是终端,docker CLI 直接拒绝启动; docker compose exec 默认带 -t,所以 compose 场景更容易踩。
解法
- 非交互命令去掉
-t:docker exec <c> sh -c "命令";需要 stdin 管道时只留-i:
echo 数据 | docker exec -i <c> sh -c "cat"(实测均可)。
- compose 场景:
docker compose exec -T <service> 命令(-T = 禁用 TTY);
旧版 v1 可加 export COMPOSE_INTERACTIVE_NO_CLI=1,但新版本以 -T 为准。
- 判断 TTY:脚本里可用
[ -t 0 ]检测 stdin 是否是终端,条件决定是否加-t。 - 容器没在跑:先
docker start <c>或docker logs <c>看为什么退出(exit code 1 的容器先排查启动失败原因,别盲目 start)。 - 没有目标命令:先
docker exec <c> ls /bin /usr/bin看镜像里有什么;alpine 用sh,要 bash 就换 ubuntu/debian 镜像,或docker exec <c> sh -c "命令"。 - 交互式进入容器(真终端):
docker exec -it <c> sh,保持 -it 不变。
验证证据
- 本地实测(2026-08-29,WSL Ubuntu,docker 29.1.3,alpine:3.20):
- 复现:
docker exec -it <c> sh </dev/null(非 TTY)→the input device is not a TTY(exit 1),与 compose#7306 同款; - 修复 1:
echo hello | docker exec -i <c> sh -c "cat"→ hello(exit 0); - 修复 2:
docker exec <c> sh -c "echo noninteractive-ok"→ 正常(exit 0); - 形态 2:容器 stop 后
docker exec <c> echo x→container ... is not running(exit 1); - 形态 3:
docker exec <c> bash -c ...→exec: "bash": executable file not found in $PATH(exit 127); - 清理:容器删除无残留。
- 实测复现日志(2026-08-29)
相关 canonical
按「前置 → 同根 / 同域 / 相关」读:
- 前置(先排除):
- Docker daemon 连不上(前置:exec 报连接错先查 daemon)
适用工具 (1)
本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。
docker · 外部工具来源与证据
← 编码代理与终端 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡