Docker номер строки в терминале меняется внутри докера

Я хотел бы знать, как изменить следующее поведение. Допустим, у моего терминала 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 номер строки в терминале меняется внутри докера

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

5 ответов

Решение

Обновление, сентябрь 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" ]

С:

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.

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

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

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

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

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

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

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

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

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 в прикрепленном сеансе работает просто отлично.
(На 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):

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


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 и установить начальный размер консоли, используя это значение?

Кенфе-Микаэль Лавентур ( mlaventure ), и новый патч может сделать это в Docker 1.13.

ОБНОВИТЬ

Теперь вы можете установить 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

Теперь вы можете получить доступ к 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;

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

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

Это было исправлено в докере 18.06: https://github.com/moby/moby/issues/33794

Я только что проверил с версией 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

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

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

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

Любой случай (1) или (3) достаточно, чтобы объяснить поведение; введение задержек и гонок не делает этого.

Другие вопросы по тегам