这是一个有效的命令:
$ echo 'hi there' | docker run -i ubuntu cat
hi there
这是一个以错误消息响应的命令:
$ echo 'hi there' | docker run -it ubuntu cat
the input device is not a TTY
我想弄清楚这里到底发生了什么。而不仅仅是“删除 -t 就可以修复”。
我知道 的docker run
选项-t
代表“分配伪 TTY”,并且我已经阅读TTY 的历史概述,但它并没有帮助我了解这里违反了什么样的合同。
答案1
回答较晚,但可能会对某人有帮助
docker run/exec -i
将会把容器内部命令的 STDIN 连接到容器docker run/exec
本身的 STDIN。
所以
docker run -i alpine cat
给出一个等待输入的空行。输入“hello”,您将得到一个回显“hello”。容器将不会退出,直到您发送 CTRL+D,因为主进程cat
正在等待来自无限流的输入,该流是 的终端输入docker run
。- 另一方面
echo "hello" | docker run -i alpine cat
将打印“hello”并立即退出,因为cat
注意到输入流已结束并自行终止。
docker ps
如果您在退出上述任一操作后尝试,您将找不到任何正在运行的容器。在这两种情况下, cat
docker 本身都已终止,因此 docker 已终止容器。
现在对于“-t”,这告诉docker内部的主进程它的输入是一个终端设备。
所以
docker run -t alpine cat
会返回一个空行,但如果您尝试输入“hello”,则不会得到任何回显。这是因为虽然cat
连接到终端输入,但此输入未连接到您的输入。您输入的“hello”未到达 的输入cat
。cat
正在等待永远不会到达的输入。echo "hello" | docker run -t alpine cat
也会给你一个空行,并且不会在 CTRL-D 上退出容器,但你不会得到回显“hello”,因为你没有通过-i
如果您发送 CTRL+C,您将恢复 shell,但如果您docker ps
现在尝试,您会看到cat
容器仍在运行。这是因为cat
仍在等待从未关闭的输入流。我发现 单独使用-t
而不与 结合使用没有任何有用的用途-i
。
现在,将-it
合并起来。这告诉 cat 它的输入是一个终端,同时将此终端连接到 的输入, 的输入docker run
是一个终端。docker run/exec
在将其传递给 之前,将确保其自身的输入实际上是一个 tty cat
。这就是为什么input device is not a TTY
如果您尝试,您将得到一个echo "hello" | docker run -it alpine cat
,因为在这种情况下, 的输入本身是来自上一个 echo 的管道,而不是执行的docker run
终端docker run
最后,为什么你需要传递-t
if-i
来完成将你的输入连接到 的输入的技巧cat
?这是因为如果输入是终端,命令会以不同的方式处理输入。这也最好通过示例来说明
docker run -e MYSQL_ROOT_PASSWORD=123 -i mariadb mysql -uroot -p
将提示您输入密码。如果您输入密码,则会打印出字符。docker run -i alpine sh
将返回一个空行。如果您键入类似命令,ls
则会获得输出,但不会获得提示或彩色输出。
在最后两种情况下,您会得到这种行为,因为mysql
以及shell
没有将输入视为 tty,因此没有使用 tty 特定的行为,例如屏蔽输入或为输出着色。
答案2
答案3
tty 表示您有一个终端,这是 xterm 或众多 linux 命令行界面之一提供的功能。它需要与之关联的键盘和文本输出界面。需要它的典型原因是支持彩色文本输出、处理各种组合键(如箭头键)以及能够在屏幕上移动光标。
当您将命令通过管道传输到 docker 中(如您的echo
示例所示),该管道就是输入,并且该管道没有 tty 接口,它只是一个文本流。尝试用它创建 tty 将失败,如错误消息所示。