40

我想知道如何改变以下行为。假设我的终端有28行。然后我使用以下命令:

$ tput lines # my terminal
28
$ docker run  --rm  -it ubuntu:16.04 tput lines  # docker container
24  ## WHY??
$ docker run  --rm  -it ubuntu:16.04 bash # docker container inside command
root@810effa2777c:/# tput lines
28

如您所见,即使所有结果都应该是28,当我调用容器时,docker run --rm -it ubuntu:16.04 tput lines尽管我的终端大小,它总是给我 24 。这不仅适用于 ubuntu 容器,我也尝试过使用 debian ( docker run --rm -it debian tput lines) 并且得到相同的结果 24。

这样做的目的是使用考虑到终端中的行的mdp 演示工具。当我的实现失败时,我尝试了其他人的docker 实现,但我遇到了同样的错误。

这是我在图像中的错误:

Docker 终端中的 Docker 行数在 docker 内部发生变化

有谁知道它可能是什么以及如何解决?

4

6 回答 6

45

更新

您现在可以goinside使用以下命令安装命令行工具:

sudo npm install -g goinside

并进入具有适当终端大小的 docker 容器:

goinside docker_container_name

goinside 背后的逻辑

感谢@VonC 的回答,我们用一个简单的 bash 片段解决了这个问题~/.profile

goinside(){
    docker exec -it $1 bash -c "stty cols $COLUMNS rows $LINES && bash";
}
export -f goinside

现在您可以进入 docker 容器,而不会出现终端大小问题:

$ goinside containername


记得source ~/.profile使用该goinside功能之前。


在 bash 中启用自动完成

(因为它在下面的评论之一中共享)如果你想启用自动完成,goinside你可以使用这个片段.profile

goinside(){
    docker exec -it $1 bash -c "stty cols $COLUMNS rows $LINES && bash";
}
_goinside(){
    COMPREPLY=( $(docker ps --format "{{.Names}}" -f name=$2) );
}
complete -F _goinside goinside;
export -f goinside;

在 zsh 中启用自动完成

如果您将zsh其用作默认终端,则可以在~/.zshrc文件中使用此代码段:

autoload bashcompinit
bashcompinit
goinside(){
    docker exec -it $1 bash -c "stty cols $COLUMNS rows $LINES && bash";
}
_goinside(){
    COMPREPLY=( $(docker ps --format "{{.Names}}" -f name=$2) );
}
complete -F _goinside goinside;
export goinside;
于 2018-03-14T15:20:35.790 回答
18

2018 年 9 月更新:检查 docker 18.06 是否有相同的问题(它不应该,在moby/mobyissue 33794之后,以及moby/mobyissue 35407PR 37172,18.06发行说明的一部分)。


2016 年:

Ubuntu Dockerfile包括:

CMD ["/bin/bash"]

这意味着默认值ENTRYPOINTsh -c(我怀疑在会话tput line中效果很好,因为tput使用数据库,它可能只为该图像中的 bash 设置)shterminfo

您可以尝试覆盖ENTRYPOINTbash -c检查是否效果更好。

但是,这在命令行中不起作用:

docker run --entrypoint /bin/bash --rm  -it ubuntu:16.04 -i -c 'tput lines'
24

我将检查定义自定义图像的选项。

FROM ubuntu:16.04
ENTRYPOINT ["/bin/bash", "-c"]

结果是一样的:

docker run --rm  -it u 'tput lines'
24

然而,这“有效”:

FROM ubuntu:16.04
ENTRYPOINT [ "/bin/bash" ]

和:

docker@default:/c/Users/vonc/prog/testsu$ docker run --rm  -it u -i -c 'ls; tput lines'
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
48

可能存在同步问题,因为同一命令有时会返回 24。

实际上,以下总是返回“not 24”:

FROM ubuntu:16.04
ENTRYPOINT [ "/bin/bash", "-l", "-i", "-c" ]

docker run --rm  -it u -c 'sleep 0.1; ls; tput lines'
48

OP silgon评论中提出:

docker run --rm -it --entrypoint /bin/bash ubuntu:16.04 -c "sleep 0.1 && tput lines"

正如BMitch评论如下

鉴于 sleep 的成功,我怀疑 docker 使用运行命令启动容器,一旦启动,客户端就会连接到正在运行的容器。通常需要几毫秒的时间。

这给了我另一个想法:

docker@default:/c/Users/vonc/prog/testsu$ 
docker run --entrypoint='/bin/bash' --name ub -d -it ubuntu:16.04
  0d9b8783afbb5e3ff4232da071d3f357985351ea1ce4d142bf6617ac456fb76b
docker@default:/c/Users/vonc/prog/testsu$ 
d attach ub
  root@0d9b8783afbb:/# tput lines
  48
  root@0d9b8783afbb:/# exit
exit
docker@default:/c/Users/vonc/prog/testsu$ drmae
0d9b8783afbb5e3ff4232da071d3f357985351ea1ce4d142bf6617ac456fb76b

tput lines附加会话中的A工作得很好。
(关于drmae别名,请参阅“如何删除旧的和未使用的 Docker 映像”)


thajeztah在评论中补充道:

创建容器,然后使用默认值 ( 80x24) 启动,之后 (when -it) 附加一个会话。
会话正在指定终端的大小;

请参阅“调整容器 TTY 的大小”API。

 DEBU[0244] Calling POST /v1.25/containers/c42fd5c4eb79c06fd7f9912b8359022f7d93887afbb33b57a67ed8bb7bfee4‌​3a/resize?h=46&w=221 

有关更多信息,请参阅docker 问题 25450
它与问题 10341 "Container create or start should accept height/width params"相关。Aleksa Sarai(cyphar)补充道(2016 年 9 月):

这实际上在运行时规范( opencontainers/runtime-spec PR 563)中再次弹出。
基本上,由于 Windows 需要能够在第一次启动时设置控制台大小,我们最终可能会为所有平台添加它


OP silgon指出以下代码api/client/container/run.go

// Telling the Windows daemon the initial size of the tty during start makes
// a far better user experience rather than relying on subsequent resizes
// to cause things to catch up.
if runtime.GOOS == "windows" {
    hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.GetTtySize()
}

带着逻辑问题:

在 Linux 上也使用此属性并使用该值设置初始控制台大小是否有意义?

Kenfe-Mickaël Laventure ( mlaventure)就在上面,一个新的补丁可以使它成为Docker 1.13

于 2016-08-08T09:14:24.190 回答
5

在容器内运行 bash 而不会遇到线路问题的好方法

docker exec -e COLUMNS="`tput cols`" -e LINES="`tput lines`" -ti container bash
于 2019-01-22T14:28:18.227 回答
1

关于sh与 terminfo 的评论在很大程度上是无关紧要的。相关部分(在给定的答案中不清楚)是命令的执行方式。 tput按以下顺序检查三个特征(使用setupterm):

  1. 来自 terminfo 数据库的终端大小(许多描述没有提供此信息,但使用TERM=xterm24 x 80
  2. 如果它可以从操作系统获取该信息,则为实际行数(即当前窗口大小),以及
  3. LINES环境COLUMNS变量。

在没有交互式 shell 的情况下运行的命令可以以排除当前窗口大小的方式执行。例如,这是ssh-t选项)的一个功能。此外,Docker 可以设置LINESCOLUMNS变量(尽管毫无意义)。

情况(1)或(3)都足以解释行为;引入时间延迟和比赛并不能做到这一点。

于 2016-09-20T12:23:44.397 回答
1

它已在 docker 18.06 中修复:https ://github.com/moby/moby/issues/33794#issuecomment-406814439

于 2018-09-11T13:13:57.523 回答
1

我刚刚用 version 测试过Docker version 18.06.1-ce, build e68fc7a。它似乎有同样的问题。然而,github issue中的一个人给出了一个实用的解决方法

docker run --rm -it -e COLUMNS=$COLUMNS -e LINES=$LINES -e TERM=$TERM -it ubuntu:16.04 tput lines
于 2018-10-26T21:32:52.493 回答