Советы пользователям

Documentation
Login

Title: Screen terminal multilexor

Author: Vitus Wagner

Введение

Когда-то давно, пользователи подключались к компьютерам с помощью телетайпов — этаких электрических пишущих машинок, которые всё, что набирается на клавиатуру, отправляли по проводу, а то, что приходило из провода — печатали на бумагу. Именно от слова teletype происходит аббревиатура tty, до сих пор использующая для обозначения специальных файлов-устройств, взаимодействующих с пользователем.

Зачастую эти телетайпы подключались к компьютеру не напрямую к последовательному порту, а через телефонную линию и модем.

Потом появились терминалы, которые точно так же, как и телетайпы, подключались к последовательным портам, но полученный от компьютера текст выводили уже на экран, а не на бумажную ленту.

И когда появились наконец графические оконные системы, то программы, изображающие в окошке примерно то же самое, что было бы изображено на экране терминала, стали называть эмуляторами терминалов.

Кроме того, примерно тогда же появились сети TCP/IP, а в них программы, позволяющие подключиться к удалённому компьютеру по сети, и работать так же как будто пользователь подключился по последовательному порту с помощью терминала.

Эти программы, точно так же как графические эмуляторы терминалов, пользуются имеющимся в ядре механизмом псевдотерминалов, то есть устройством, которое можно открыть и читать-писать туда, а система будет воспринимать это так, как будто это последовательный порт, к которому подключён терминал, и запущенные программы будут полагать что общаются с терминалом, а не с другой программой по пайпу.

Это сразу влияет на поведение программ — они перестают накапливать вывод большими кусками, ведь за терминалом сидит нетерпеливый человек, начинают пользоваться различными сигналами управления модемом, присутствующими в последовательном интерфейсе и так далее.

А в случае разрыва соединения им посылается сигнал SIGHUP, название которого происходит от hang up, повесить телефонную трубку.

Но несмотря на то, что современные компьютерные сети намного надёжнее тех старинных телефонных сетей, иногда очень хочется иметь возможность подсоединиться к машине запустить сессию, потом отсоединиться и через некоторое время вернуться, попав в ту же сессию, где продолжает выполняться какая-то долгоиграющая программа.

Ещё бывает хочется, запустив какую-то программу на работе, разлогиниться, пойти домой, а из дома зайти по ssh и посмотреть, что она там делает.

Этими и многими другими возможностями обладают программы-мультиплексоры терминалов, screen и tmux.

Ведь если у нас есть программный интерфейс, который позволяет программе притвориться терминалом, кажется довольно естественным написать программу, которая, будучи сама запущенной в терминале, будет управлять несколькими терминальными сессиями, где работают какие-то другие программы.

Именно такой программой и является screen.

Простейшие команды

Запустив screen без параметров мы получим на экране текст GNU-лицензии c предложением «нажмите Return для завершения». Нажав на Return мы вновь увидим шелловское приглашение. Что, всё кончилось? Нет. Наберём echo $TERM и увидим что тип терминала у нас теперь не xterm, или что там было, а screen, а значение переменной SHLVL увеличилось на единицу. Мы внутри screen.

Теперь мы можем создать новое окно: нажмём Ctrl-A, а потом c (от create). И переключаться по кругу между ними Ctrl-A n (Next) вперёд или Ctrl-A p (Previous) — назад. Вообще все команды screen начинаются с кнопки Сtrl-A. (Её можно переопределить в файле конфигурации, но мы пока этого делать не будем).

Нажав Сtrl-A d (detach), мы отсоединяем screen от текущего терминала и отправляем в фоновый режим со всеми его сессиями. Теперь можно спокойно завершать сеанс, то что запущено под screen будет продолжать работать.

Мы можем посмотреть, есть ли на машине запущенный screen (и сколько их), набрав screen -ls. Можем присоединиться, набрав screen -r. Если запущенных скринов несколько, нужно будет после -r указать первые несколько цифр pid того скрина, к которому мы хотим присоединиться (pid в данном случае то, что показывает screen -ls.

Прицепиться таким образом к уже прицепленному к какому-то терминалу screen-у нельзя. Если мы хотим работать с одним и тем же набором виртуальных сессий из нескольких терминалов, надо использовать -x, а не -r.

Есть правда еще возможность отключить screen от того терминала, где он сейчас и подключить к текущему. Для этого вместе с -r надо указать -d.

Всегда под screen

На мой взгляд, самым главным свойством этой программы является то, что в случае обрыва сетевого соединения, работающий процесс не прерывается, и потом к нему можно вернуться.

К сожалению, приучить себя вручную запускать screen перед тем, как запустить длительный процесс, который жалко потерять при обрыве связи, мне за четверть века так и не удалось. В конце, концов я решил исследовать вопрос, а нельзя ли запускать скрин автоматически, при логине в систему.

screen как login shell

В принципе, можно даже прописать screen в /etc/passwd в качестве login shell. Он на это рассчитан. Но что вы будете в этом случае делать, если надо неинтерактивно запустить команду по ssh?

Да и описанные в следующем разделе техники работы с ssh-агентом требуют некоторых манипуляций снаружи от screen.

Поэтому лучше запустить screen из ~/.profile, который не отрабатывается при неинтерактивном логине.

При этом надо сделать так, чтобы сессия у нас появлялась независимо от того, был запущен screen на целевой машине, или нет в момент, когда мы туда залогинились.

Поэтому следует использовать комбинацию ключиков -xRR.

Далее есть два варианта:

  1. Мы цепляемся к последней сессии, если таковая была, и новую в случае чего создадим сами, нажав Ctrl-A С. Тогда нужно указать параметры -xRR. Это как раз то поведение, которое будет если screen прописать в /etc/passwd.
  2. Мы всегда стартуем с чистого листа. Хотя и присоединяемся к старому screen, но хотим чтобы для нового логина была создан новая сессия. А если понадобится старая, то мы уж как-нибудь внутри screen до неё доберёмся, либо перебором по кругу, либо вызовом списка окон. Тогда к -xRR надо добавить -p +.

Если мы работаем на машине локально, в какой-нибудь оконной среде, то в ней имеет смысл завести специальную иконку (например ${HOME}/.local/share/applications/screen.desktop для запуска эмулятора терминала со screen -xRR -p + внутри и ей-то и пользоваться обычно для открытия консольного окна. (хотя иконку, позволяющую открыть терминал без screen желательно все же под рукой иметь).

screen и ssh-agent

Если не предпринимать специальных мер, то ssh-агент доступен в screen до тех пор, пока мы работаем в той же сессии, из которой был запущен этот screen. Это связано с тем, что для каждой сессии sshd создаёт новый авторизационный сокет со случайным именем. И что-то в этом подходе правильное есть. Не то чтобы повышенная безопасность, но хотя бы чтобы не путались сессии. А то ведь закрывать мы их будем далеко не в том порядке, как открывали.

Но вот для того, чтобы давным-давно запущенная сессия screen смогла получить доступ к агенту, которого мы отфорвардили вот сейчас, подключаясь к этой сессии, имя сокета должно быть неизменным.

Можно, например, при заходе по ssh всегда создавать симлинк ${HOME}/.ssh/auth_sock на текущий сокет, и выставлять переменную SSH_AUTH_SOCK на него до запуска screen -xRR.

Тогда внутри screen ssh всегда будет ходить к сокету последней по времени открытия сессии. Ну если мы её вдруг закрыли раньше, и в остальных сессиях доступ к агенту пропал — не беда. Открываем у себя на локальной машине новую сессию и заходим оттуда.

Если у нас домашняя директория разделяется между несколькими машинам, то ситуация чуточку сложнее. Поскольку unix-domain сокеты вещь локальная, и на каждой из машин, где у нас используется данный ${HOME} надо использовать свой уникальный.

А если у нас используются chrooted environments, например schroot, то всё ещё хуже. Хорошего решения для schroot я так и не нашёл. Поэтому конец моего .profile выглядит так:

if [ -z "${debian_chroot:-}" ]; then
	# Set up SSH auth socket for screen
	if [ -n "$SSH_AUTH_SOCK" -a -S "$SSH_AUTH_SOCK" -a ! -h "$SSH_AUTH_SOCK" ]; then
		ln -sf "$SSH_AUTH_SOCK" ${HOME}/.ssh/auth_sock_$(hostname)
	fi
	SSH_AUTH_SOCK=${HOME}/.ssh/auth_sock_$(hostname)
	export SSH_AUTH_SOCK
	# start screen
	exec screen -xRR
fi

То есть сначала мы проверяем, что мы не в chroot, и если в нем, то вообще все это пропускаем.

Аналогичный симлинк имеет смысл создавать и из .xsessionrc, а то вдруг захочется прицепиться из графической среды к сессии screen, созданной заходом по ssh или наоборот.

Автозапуск программы с текстовым интерфейсом

Иногда очень хочется запустить какую-нибудь программу с текстовым интерфейсом так, чтобы она сразу ушла в background но потом можно было подцепиться. Или вообще стартовала при загрузке системы. screen даёт и такую возможность.

Во-первых, все аргументы командной строки, которые не являются опциями screen будут проинтерпретированы как имя команды и её параметры.

Во-вторых, сочетание флагов -d -m позволяет сделать как раз то что надо - создать новую сессию и отправить ее в фон.

В некоторых случаях, например при старте из юнита systemd может оказаться более интересным вариант с большой буквой -D. Который не уходит в бэкграунд сам, предоставляя управлять этим запускающему.

Многооконность

Увидев, что программа позволяет работать с несколькими сессиями одновременно, современный пользователь тут же задаёт вопрос: «А одновременно их увидеть можно?». Конечно же можно.

Можно поделить текущее окно пополам по горизонтали Ctrl-A S, можно по вертикали Сtrl-A |.

Можно переключаться между окнами с помощью Ctrl-A tab, можно закрыть текущее Ctrl-A X, можно — все кроме текущего Ctrl-A Q.

Скрин и кодировки

Сейчас, во времена всепобеждающей utf-8 мало кто помнит, что были времена, когда было необходимо работать с несколькими кодировками русского языка. По-моему их число доходило до семи, хотя сейчас в /usr/share/locale остались следы от 4, не считая utf-8.

Но иногда до сих пор бывает, что необходимо немножко поработать в какой-нибудь 8-битной кодировке кириллице.

Оказывается, скрину можно указать, что вот в этом окне следует использовать другую кодировку. Отдельной комбинации клавиш для этого нет но через Сtrl-A : можно попасть в командную строку где указать команду encoding. Конечно, запущенному в окне процессу шелла ещё придётся потом объяснить, что локаль у него сегодня не ru_RU.UTF-8, а, скажем, ru_RU.CP1251.

В принципе возможно и наоборот - запустить скрин в неюникодном терминале, а потом создать в нем юникодную сессию. Но это разве что у вас аппаратный VT-420 где-то завалялся. И то, вы не всякую букву в нём увидите.

Ещё screen предоставляет возможность вводить символы, которых нет на клавиатуре. Можно восьмеричным кодом, если вы его помните, можно с помощью digraph-ов. Конечно, у него не столько предопределенных диграфов, как у vim, но можно и своих в .screenrc поопределять.

Буфер обмена и лог сессии

Весь вывод, попадающий в сессию screen, попадает не сразу на экран пользователя, а сначала внутрь очень богатой разными функциями программы.

Соотвественно, возможны варианты:

  1. Прокрутить экран назад Ctrl-A Esc или Ctrl-A [
  2. Выделить кусок экрана и скопировать в буфер обмена - переходим в режим прокрутки экрана, а потом клавишей Enter выделяем начало блока и ещё одним нажатием Enter - конец.
  3. Вставить содержимое буфера (возможно в другую сессию) как будто оно введено с клавиатуры Ctrl-A ] - открывающая квадратная скобка копирует, закрывающая — вставляет.
  4. Записать буфер обмена в файл. Ctrl-A >. К сожалению, имя файла для этой и следующей операции не спрашивают прямо по ходу дела, и если вас не устраивает умолчание, нужно заранее задать имя Ctrl-A :bufferfile имя
  5. Прочитать в буфер из файла (чтобы вставить потом в текущую сессию). Ctrl-A <
  6. Там где больше и меньше, там и равно. Ctrl-A = стирает файл, используемый для двух предыдущих операций.
  7. Включить ведение лога сессии в файл Ctrl-A H. Имя лог-файла тоже нужно задавать заранее, либо с помощью команды logfile в командой строке screen, либо с помощью опции командной сроки.

Работа с последовательным портом

Иногда и в наше время возникает необходимость поработать с устройством, работающим не с псевдо, а реальными последовательными портами.

uucp и входящую в её комплект cu, которой пользовались для логина на удалённые компьютеры через телефонные линии в прошлом веке, нынче на компьютерах не найдёшь. Но screen умеет и это. Ему нужно указать вместо имени команды имя специального файла последовательного порта и он будет работать терминальной программой. Ещё в нем есть встроенный telnet-клиент, но это по нынешним временам совсем экзотика.

Наводим красоту

Начав использовать screen всегда, как описано выше, я столкнулся с тем, что совершенно неочевидно, сколько разных скринов на разных машинах отделяет меня от программы, и сколько букв a мне надо набрать после Ctrl-A, чтобы последующая буква c создала мне сессию на нужной машине.

Этот вопрос решился двумя строчками в ${HOME}/.screenrc:

caption always
caption string "%H %w"

Теперь каждая из машин, через которые пролёг мой путь до запущенной в данный момент программы, оставляет след на экране в виде нижней строчки с именем машины (%H) и списком окон (%w).

Иногда бывает, что присоединившись к существующей сессии, пользователь обнаруживает, что она занимает только часть окна — где-то в другом месте она присоединена к терминалу меньшего размера. Или наоборот — видна только часть сессии. Можно это исправить, нажав Ctrl-A F.

Управление screen изнутри screen

Иногда бывает нужно из скрипта, который выполняется внутри сессии screen что-то сделать с тем screen в котором он выполняется.

Самый простой вариант - это открыть новое окно и запустить в нём команду. Это делается так же, как просто запуск команды в screen.

screen команда

Он сам разберётся, что запущен внутри другого screen и вместо создания нового экземпляра откроет окно в старом.

Можно поменять заголовок окна screen

screen -X title "Новый заголовок"

Можно даже запросить кое-какие параметры

screen -Q title

значения будут выведены на stdout и их можно анализировать в скрипте.

Заключение

Вообще-то я не описал и десятой части возможностей screen. Читайте man, экспериментируйте.