Докерное число строк в терминале, изменяющееся внутри докера

Я хотел бы знать, как изменить следующее поведение. Скажем, мой терминал имеет 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
[email protected]:/# 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 presentation, который учитывает строки в вашем терминале. Когда моя реализация завершилась неудачно, я попробовал другую команду docker implementation, но я столкнулся с той же ошибкой.

Здесь моя ошибка в изображении:

Докерное число строк в терминале, меняющемся внутри докера

Кто-нибудь знает, что это может быть и как это можно решить?

Ответ 1

Обновление, сентябрь 2018: проверьте, имеет ли Docker 18.06 ту же проблему (не должно быть, после выпуска moby/moby 33794, а также выпуска moby/moby 35407 и PR 37172, часть заметок о выпуске 18.06).


2016:

Ubuntu Dockerfile включает в себя:

CMD ["/bin/bash"]

Это означает, что ENTRYPOINT по умолчанию является sh -c (и я сомневаюсь, что tput line хорошо работает в сеансе sh, поскольку tput использует базу данных terminfo, которая может быть установлена только для bash в этом образе)

Вы можете попробовать переписать ENTRYPOINT с помощью bash -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" ]

С:

[email protected]:/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.

На самом деле, следующие всегда возвращают "не 24" с:

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

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

ОП силгон предлагает в комментариях:

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

Как комментирует BMitch ниже:

Учитывая успех сна, я подозреваю, что докер раскручивает контейнер с запущенной командой, и когда клиент работает, он подключается к работающему контейнеру. Обычно это то, что занимает миллисекунды.

Это дало мне другую идею:

[email protected]:/c/Users/vonc/prog/testsu$ 
docker run --entrypoint='/bin/bash' --name ub -d -it ubuntu:16.04
  0d9b8783afbb5e3ff4232da071d3f357985351ea1ce4d142bf6617ac456fb76b
[email protected]:/c/Users/vonc/prog/testsu$ 
d attach ub
  [email protected]:/# tput lines
  48
  [email protected]:/# exit
exit
[email protected]:/c/Users/vonc/prog/testsu$ drmae
0d9b8783afbb5e3ff4232da071d3f357985351ea1ce4d142bf6617ac456fb76b

tput lines в прикрепленном сеансе работают просто отлично.
(Об псевдониме drmae см. " Как удалить старые и неиспользуемые образы Docker ")


thajeztah добавляет в комментариях:

контейнер создается, затем запускается со значениями по умолчанию (80x24), и после этого (когда -it) присоединяется сеанс.
Сеанс определяет размер терминала;

См. API " Изменение размера TTY контейнера ".

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

Для получения дополнительной информации см. Выпуск докера 25450.
Это связано с проблемой 10341 "Создание или запуск контейнера должны принимать параметры высоты/ширины". Алекса Сарай (cyphar) добавляет (сентябрь 2016):

Это снова всплыло в спецификации времени выполнения (opencontainers/runtime-spec PR 563).
По сути, поскольку Windows требуется возможность установки размера консоли при первом запуске, мы можем добавить ее для всех платформ.


Силикон OP указывает на код в 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.

Ответ 2

ОБНОВИТЬ

теперь вы можете установить goinside командной строки goinside с помощью:

sudo npm install -g goinside

и зайдите внутрь контейнера докера с соответствующим размером терминала с:

goinside docker_container_name

Логика за goinside

благодаря ответу @VonC у нас есть решение этой проблемы с простым фрагментом bash, который мы вставляем в ~/.profile:

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

теперь вы можете попасть внутрь контейнера докеров без проблем с размером терминала:

$ goinside containername


запомните source ~/.profile goinside 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;

Ответ 3

Комментарии о sh в сравнении с terminfo в значительной степени неактуальны. Соответствующая часть (непонятная в данном ответе) - это способ выполнения команды. tput проверяет три функции в следующем порядке (используя setupterm):

  • размер терминала из базы данных terminfo (многие описания не дают этой информации, но с TERM=xterm она 24 80),
  • фактическое количество строк, если оно может получить эту информацию из операционной системы (то есть текущий размер окна) и
  • переменные окружения LINES и COLUMNS.

Команда, которая запускается без интерактивной оболочки, может быть выполнена таким образом, чтобы исключить получение текущего размера окна. Например, это функция ssh (опция -t). Кроме того, было бы (хотя и бессмысленно) для Docker устанавливать переменные LINES и COLUMNS.

Для объяснения поведения достаточно одного (1) или (3); введение временных задержек и рас не делает этого.

Ответ 5

Я только что протестировал с версией Docker version 18.06.1-ce, build e68fc7a. Кажется, есть та же проблема. Тем не менее, один из парней в выпуске github дал практический обходной путь:

docker run --rm -it -e COLUMNS=$COLUMNS -e LINES=$LINES -e TERM=$TERM -it ubuntu:16.04 tput lines

Ответ 6

Хороший способ запустить bash внутри контейнера без проблем со строкой (а не запускать exec дважды):

docker exec -e COLUMNS="'tput cols'" -e LINES="'tput lines'" -ti container bash