254 lines
12 KiB
ReStructuredText
254 lines
12 KiB
ReStructuredText
:title: Терминальные мультиплексоры
|
||
:date: 19 Oct 22
|
||
|
||
===========================
|
||
Терминальные мультиплексоры
|
||
===========================
|
||
|
||
Терминальный мультиплексор — это утилита, которая позволяет запускать
|
||
несколько сессий оболочки в рамках одной сессии и также отсоединять
|
||
собственную сессию от оболочки.
|
||
|
||
Для пояснения зачем это нужно я процитирую русскую Википедию:
|
||
|
||
Это полезно для работы с несколькими программами из командной строки, а
|
||
также для запуска программ на удаленном сервере.
|
||
|
||
Возможно звучит сложно, но всё проще чем кажется. В этой заметке я попробую
|
||
наглядно объяснить концепцию и приёмы работы с мультиплексорами терминала.
|
||
|
||
Сейчас распространено два мультиплексора терминала:
|
||
|
||
* `GNU screen <https://www.gnu.org/software/screen/>`_
|
||
* `tmux <https://github.com/tmux/tmux/wiki>`_
|
||
|
||
Рано или поздно вы с одним из них столкнётесь.
|
||
|
||
Матчасть
|
||
========
|
||
|
||
Вот основные концепции, которые использются в упомянутых утилитах.
|
||
|
||
Сессия
|
||
Непосредственно сессия мультиплексора терминала. Сессий может быть
|
||
множество, их можно создавать, закрывать, отсоединяться от них или
|
||
присоединяться, задать имя.
|
||
|
||
Окно
|
||
В любой сессии мультиплекcора есть как минимум одно окно. Если закрыть
|
||
последнее окно, то закроется вся сессия. В каждом окне запускается новая
|
||
сессия командной оболочки (Bash, Zsh и пр.).
|
||
|
||
Окон также может быть множество. К ним применимы те же действия, что и к
|
||
сессиям, кроме отсоединения.
|
||
|
||
Между окнами можно переключаться с помощью горячих клавиш.
|
||
|
||
Область экрана (или панель)
|
||
Каждое окно можно разбить на несколько областей. В каждой области будет
|
||
запущена новая сессия оболочки.
|
||
|
||
Делить окно можно по вертикали и горизонтали. Размеры областей обычно
|
||
можно отрегулировать. По умолчанию экран делится пополам.
|
||
|
||
Здесь надо отметить, что в **screen** понятия окно и область экрана
|
||
смешаны. То есть область экрана приравнивается к отдельному окну, хотя
|
||
они и отображаются на одном экране.
|
||
|
||
Итак, я подключаюсь к серверу по SSH с целью запустить там какую-то команду.
|
||
После запуска команды я намерен отключиться от сервера. При этом команда
|
||
должна продолжить выполняться. Я также должен увидеть результат её выполнения,
|
||
когда вновь подключусь к серверу.
|
||
|
||
**1.** Подключаюсь по SSH:
|
||
|
||
.. code-block:: text
|
||
|
||
localhost server
|
||
+------------+ +------------------+
|
||
| ssh_client |----->| ssh_session |
|
||
+------------+ +------------------+
|
||
(you are here)----->| shell_session(0) |
|
||
+------------------+
|
||
|
||
После успешного подключения по SSH я окажусь в сессии оболочки — **(0)**.
|
||
При этом если оборвётся SSH-сессия, то я потеряю всё что в ней было.
|
||
|
||
**2.** Как только я запущу **screen** картинка примет следующий вид:
|
||
|
||
.. code-block:: text
|
||
|
||
localhost server
|
||
+------------+ +------------------+
|
||
| ssh_client |----->| ssh_session |
|
||
+------------+ +------------------+
|
||
| shell_session(0) |
|
||
+------------------+
|
||
| screen_session |
|
||
+------------------+
|
||
(you are here)----->| shell_session(1) |
|
||
+------------------+
|
||
|
||
**3.** Для примера запущу **vim**:
|
||
|
||
.. code-block:: text
|
||
|
||
localhost server
|
||
+------------+ +------------------+
|
||
| ssh_client |----->| ssh_session |
|
||
+------------+ +------------------+
|
||
| shell_session(0) |
|
||
+------------------+
|
||
| screen_session |
|
||
+------------------+
|
||
(you are here)----->| shell_session(1) |
|
||
+------------------+
|
||
| vim |
|
||
+------------------+
|
||
|
||
**4.** Теперь я отключусь (detach) от сессии **screen**:
|
||
|
||
.. code-block:: text
|
||
|
||
localhost server
|
||
+------------+ +------------------+ +------------------+
|
||
| ssh_client |----->| ssh_session | | screen_session |
|
||
+------------+ +------------------+ +------------------+
|
||
(you are here)----->| shell_session(0) | | shell_session(1) |
|
||
+------------------+ +------------------+
|
||
| vim |
|
||
+------------------+
|
||
|
||
Сессия **screen** отсоединилась от оболочки **(0)**. При этом оболочка **(1)**
|
||
продолжает работать. Сейчас можно отключиться от SSH.
|
||
|
||
Когда я подключусь к серверу вновь, то окажусть в ситуации как на схеме 4, но
|
||
после выполнения команды **screen -r** всё вернётся к состоянию как на схеме 3
|
||
и я смогу продолжить работу в **vim**.
|
||
|
||
.. admonition:: Как набирать комбинации клавиш
|
||
|
||
Вначале набрается модификатор, например ``Ctrl+a``, затем надо отпустить
|
||
клавиши и нажать на клавишу команды, например ``?``.
|
||
|
||
screen
|
||
======
|
||
|
||
**screen** просто работает. Он минималистичен и прост в использовании.
|
||
|
||
Запуск новой сессии:
|
||
|
||
.. code-block:: text
|
||
|
||
screen [-S имя_сессии]
|
||
|
||
Подключиться к запущенной сессии:
|
||
|
||
.. code-block:: text
|
||
|
||
screen -r [имя_сессии]
|
||
|
||
=============== ============================================================
|
||
Комбинация Действие
|
||
=============== ============================================================
|
||
Ctrl+a ? Показать справку
|
||
Ctrl+a : Открыть приглашение для ввода команд screen
|
||
Ctrl+a " Список окон
|
||
Ctrl+a 0 Открыть окно номер 0
|
||
Ctrl+a A Переименовать текущее окно
|
||
Ctrl+a a Отправить комбинацию Ctrl+a в текущее окно
|
||
Ctrl+a c Создать новое окно (с оболочкой)
|
||
Ctrl+a S Разделить текущую область по горизонтали
|
||
Ctrl+a | Разделить текущую область по вертикали
|
||
Ctrl+a tab Перевести фокус ввода на следующую область
|
||
Ctrl+a Ctrl+a Переключиться между текущей и предыдущей областью
|
||
Ctrl+a Esc Перейти в режим копирования (используйте Enter для выделения
|
||
текста). Также скроллинг терминала.
|
||
Ctrl+a ] Вставить текст
|
||
Ctrl+a Q Закрыть все окна, кроме текущего
|
||
Ctrl+a X Закрыть текущую область
|
||
Ctrl+a d Отсоединиться от текущей сессии
|
||
=============== ============================================================
|
||
|
||
При разделении экрана на области появится новая пустая область. Нужно
|
||
переключить на неё фокус и запустить новое окно с оболочкой ``Ctrl+a`` ``c``.
|
||
|
||
Конфигурация **screen** хранится в файле **~/.screenrc**. Я для себя написал
|
||
совсем простой конфиг, который тем не менее показывает всё что действительно
|
||
нужно.
|
||
|
||
.. code-block:: unixconfig
|
||
|
||
startup_message off
|
||
hardstatus alwayslastline
|
||
hardstatus string '%S: %-w%>(%n %t)%{-}%+w%<'
|
||
|
||
.. image:: https://i.nxhs.cloud/Swl.png
|
||
:alt: Terminal with screen
|
||
|
||
Материалы по **screen**:
|
||
|
||
* `GNU screen usage <http://gnuscreen.org/>`_
|
||
* `GNU screen - ArchWiki <https://wiki.archlinux.org/title/GNU_Screen>`_
|
||
|
||
tmux
|
||
====
|
||
|
||
**tmux** — это более новороченное решение. Он умеет почти всё то же что
|
||
**screen** + имеет дополнительные фичи вроде управления мышью.
|
||
|
||
Запустить новую сессию:
|
||
|
||
.. code-block:: text
|
||
|
||
tmux [new -s имя_сессии]
|
||
|
||
Подключиться к сессии:
|
||
|
||
.. code-block:: text
|
||
|
||
tmux a [-t имя_сессии]
|
||
|
||
=============== ============================================================
|
||
Комбинация Действие
|
||
=============== ============================================================
|
||
Ctrl+b c Создать новое окно
|
||
Ctrl+b , Переименовать окно
|
||
Ctrl+b w Список окон
|
||
Ctrl+b " Разделить текущую область по горизотали
|
||
ctrl+b % Разделить текущую область по вертикали
|
||
Ctrl+b x Закрыть текущую панель
|
||
Ctrl+b [ Перейти в режим копирования текста + скроллинг
|
||
Ctrl+b d Отсоединиться от сессии
|
||
=============== ============================================================
|
||
|
||
Мой **~/.tmux.conf**:
|
||
|
||
.. code-block:: unixconfig
|
||
|
||
set -g mouse on
|
||
set -g history-limit 5000
|
||
|
||
# vim style controls
|
||
bind h select-pane -L
|
||
bind j select-pane -D
|
||
bind k select-pane -U
|
||
bind l select-pane -R
|
||
|
||
bind -r H resize-pane -L 10
|
||
bind -r J resize-pane -D 10
|
||
bind -r K resize-pane -U 10
|
||
bind -r L resize-pane -R 10
|
||
|
||
# Copy / paste
|
||
bind b list-buffers
|
||
bind B show-buffer
|
||
bind P paste-buffer
|
||
|
||
bind-key -T copy-mode-vi v send-keys -X begin-selection
|
||
bind-key -T copy-mode-vi y send-keys -X copy-selection
|
||
bind-key -T copy-mode-vi r send-keys -X rectangle-toggle
|
||
|
||
bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel \
|
||
"xclip -i -f -selection primary | xclip -i -selection clipboard"
|