tmux глубоко: сессии, панели и терминал без мыши
Представь типичный вечер инженера: ты по SSH дебажишь сервер, запустил долгий прогон миграций, и тут падает VPN. Переподключаешься — а шелл новый, процесс убит вместе с сессией, и непонятно, на каком шаге всё умерло. Или проще: закрыл окно терминала на локальной машине — и все шесть открытых в нём процессов испарились. tmux существует ровно для того, чтобы этих историй больше не было.
tmux — терминальный мультиплексор: программа, которая внутри одного терминала даёт множество виртуальных терминалов (окон и панелей) и, главное, отделяет жизнь процессов от жизни эмулятора терминала. Сессия tmux живёт на сервере tmux, а твой терминал — просто клиент, который к ней подключается. Закрыл терминал, отвалился SSH, перезагрузил иксы — сессия как работала, так и работает; подключаешься обратно и продолжаешь с того же места.
В краткой версии учебника tmux упоминался одной строкой в списке софта. Здесь разбираем его как следует: модель сервер/сессия/окно/панель, реальный боевой конфиг с префиксом Ctrl+Space и навигацией через Alt, copy-mode в vi-стиле — и под капотом: клиент-серверную архитектуру, зачем Neovim нужны escape-time 0 и focus-events, и как буфер обмена проезжает через SSH через OSC52.
Зачем мультиплексор: четыре причины
Заголовок раздела «Зачем мультиплексор: четыре причины»Первая: выживаемость. Процессы внутри tmux — дети tmux-сервера, а не твоего терминала. SIGHUP при отвале соединения до них не доходит. Это не «удобство», это другой класс надёжности для всего долгоживущего: миграции, тейлы логов, локальные dev-серверы.
Вторая: окна и панели. Вместо десяти вкладок терминала с именами «Terminal 1..10» — именованные окна и разделённые панели внутри одного экрана: слева редактор, справа снизу тесты, отдельное окно под lazygit. Тайлинг, который мы видели в Hyprland на уровне окон ОС, tmux делает внутри терминала — и это работает одинаково локально и по SSH, где никакого Hyprland нет.
Третья: единый буфер обмена. Всё, что ты скопировал в copy-mode tmux, доступно во всех панелях и окнах сессии — и, при правильной настройке, в системном буфере. Скопировать стектрейс из логов в одной панели и вставить в редактор в другой — без мыши и без «выделил — переключился — вставил».
Четвёртая: скриптинг рабочих мест. tmux полностью управляется из командной строки: tmux new-session, split-window, send-keys. Это значит, что «рабочее место проекта» — редактор, тесты, dev-сервер, логи — описывается скриптом и поднимается одной командой. Вернулся к проекту через неделю — ./dev.sh, и весь layout на месте.
#!/usr/bin/env bash# dev.sh — поднимает рабочее место pet-проекта одной командойSESSION=dashboard
# Создаём сессию в фоне, первое окно — редакторtmux new-session -d -s "$SESSION" -n codetmux send-keys -t "$SESSION:code" 'nvim .' Enter
# Сплит снизу под тесты (30% высоты)tmux split-window -v -p 30 -t "$SESSION:code"tmux send-keys -t "$SESSION:code" 'npm run test:watch' Enter
# Отдельное окно под gittmux new-window -t "$SESSION" -n gittmux send-keys -t "$SESSION:git" 'lazygit' Enter
# Подключаемся к первому окнуtmux attach -t "$SESSION:code"Модель: сервер, сессии, окна, панели
Заголовок раздела «Модель: сервер, сессии, окна, панели»Иерархия tmux строгая, и её стоит выучить один раз, потому что все команды адресуются по ней:
tmux server (один на пользователя, слушает сокет в /tmp) └── session: dashboard — «проект», отдельный контекст работы ├── window 1: code — «вкладка», полноэкранная │ ├── pane 0: nvim — разделённая область внутри окна │ └── pane 1: тесты └── window 2: git (lazygit)- Сервер — фоновый процесс, владеющий всеми сессиями. Он стартует при первом
tmux newи умирает только когда завершена последняя сессия. - Сессия — именованный контекст: обычно одна на проект или на задачу.
tmux new -s имя,tmux attach -t имя,tmux kill-session -t имя. - Окно — полноэкранная «вкладка» внутри сессии, со своим именем.
- Панель — разделённая часть окна. Окно без сплитов — одна панель.
Адресация в командах идёт через сессия:окно.панель — например, dashboard:code.1. Эту нотацию ты видел в скрипте выше.
Конфиг: префикс Ctrl+Space и навигация без префикса
Заголовок раздела «Конфиг: префикс Ctrl+Space и навигация без префикса»Дефолтный префикс tmux — Ctrl+b: клавиша, которая требует скрючить пальцы и конфликтует с «назад» в readline. Первое, что делает каждый, — меняет префикс. Боевой конфиг использует Ctrl+Space: обе руки на месте, большой палец на пробеле, никаких конфликтов с readline и Neovim.
Вторая идея конфига: самое частое не должно требовать префикса вообще. Переключение окон и фокус панелей происходит сотни раз в день — им положены прямые клавиши на Alt:
# ~/.tmux.conf — боевой конфиг с русскими комментариями
# Префикс: Ctrl+Space вместо Ctrl+bunbind C-bset -g prefix C-Spacebind C-Space send-prefix
# Vi-режим в copy-mode и статусной строкеset -g mode-keys viset -g status-keys vi
# Нумерация окон и панелей с единицы — 0 далеко на клавиатуреset -g base-index 1set -g pane-base-index 1
# Окна: Alt+цифра — прыжок, Alt+стрелки — листать, без префиксаbind -n M-1 select-window -t 1bind -n M-2 select-window -t 2bind -n M-3 select-window -t 3bind -n M-4 select-window -t 4bind -n M-5 select-window -t 5bind -n M-6 select-window -t 6bind -n M-7 select-window -t 7bind -n M-8 select-window -t 8bind -n M-9 select-window -t 9bind -n M-Left previous-windowbind -n M-Right next-window
# Сплиты: Alt+Enter — вертикальный, Alt+Shift+Enter — горизонтальныйbind -n M-Enter split-window -h -c "#{pane_current_path}"bind -n M-S-Enter split-window -v -c "#{pane_current_path}"
# Фокус панелей: Ctrl+Alt+стрелкиbind -n C-M-Left select-pane -Lbind -n C-M-Right select-pane -Rbind -n C-M-Up select-pane -Ubind -n C-M-Down select-pane -D
# Ресайз панелей: Ctrl+Alt+Shift+стрелкиbind -n C-M-S-Left resize-pane -L 5bind -n C-M-S-Right resize-pane -R 5bind -n C-M-S-Up resize-pane -U 3bind -n C-M-S-Down resize-pane -D 3
# Окна и сессии через префикс: c — создать, r — переименовать, k — убитьbind c new-window -c "#{pane_current_path}"bind r command-prompt -I "#W" "rename-window '%%'"bind k kill-windowbind P switch-client -p # предыдущая сессияbind N switch-client -n # следующая сессия
# Шпаргалка по всем биндам: префикс+?bind ? list-keys
# Для Neovim: мгновенный Esc и события фокуса (разбор ниже)set -sg escape-time 0set -g focus-events on
# 256 цветов и truecolor для современных темset -g default-terminal "tmux-256color"set -ag terminal-overrides ",xterm-256color:RGB"
# OSC52: копирование из tmux в системный буфер, в том числе через SSHset -g set-clipboard onНесколько деталей, которые здесь важны. bind -n — бинд без префикса (n = no prefix): именно он позволяет жать Alt+3 напрямую. #{pane_current_path} в сплитах — новая панель открывается в той же директории, где ты сейчас, а не в домашней: мелочь, без которой каждый сплит начинается с cd. Префикс+? выводит все биндинги — встроенную шпаргалку, которая всегда соответствует твоему конфигу.
Copy-mode в vi-стиле
Заголовок раздела «Copy-mode в vi-стиле»Прокрутка и копирование в tmux происходят в copy-mode: префикс+[ (или просто крути колесо мыши, если mouse on). С mode-keys vi навигация там — привычные hjkl, / для поиска вверх по выводу, n/N по совпадениям. Копирование: v начинает выделение (как visual-режим), двигаешься до конца, y — скопировать и выйти. Вставка из буфера tmux — префикс+].
Здесь прячется суперсила: copy-mode — это поиск по выводу любой панели. Длинный лог тестов, стектрейс, вывод git log — всё это greppable прямо в терминале, без перенаправления в файл. Нашёл нужное, v…y, переключился в панель с Neovim, вставил.
Под капотом: клиент-серверная архитектура
Заголовок раздела «Под капотом: клиент-серверная архитектура»Теперь главное — почему всё это вообще возможно. tmux состоит из двух процессов:
┌──────────────┐ unix-сокет ┌──────────────────────────┐│ tmux client │ ◄────────────► │ tmux server ││ (в твоём │ /tmp/tmux-… │ владеет сессиями, окнами││ терминале) │ │ и PTY всех процессов │└──────────────┘ │ │ клиентов может │ ┌────────┐ ┌────────┐ │ быть несколько: │ │ сессия │ │ сессия │ │ локальный терминал │ │ dash │ │ infra │ │ + SSH-подключение │ └───┬────┘ └────────┘ │ одновременно │ │ PTY: nvim, zsh… │ └───────┼──────────────────┘ ▼ настоящие процессы живут здесьСервер создаёт для каждой панели псевдотерминал (PTY) и запускает в нём твой шелл. Клиент лишь пересылает нажатия клавиш серверу и отрисовывает то, что сервер присылает обратно. Отсюда следуют все свойства:
- Клиент умер (закрыл терминал, порвался SSH) — серверу всё равно, PTY и процессы живы.
- К одной сессии могут подключиться два клиента сразу: ты с десктопа и коллега по SSH видите и правите одно и то же. Это готовый парный дебаг без всякого screen sharing.
tmux attachпосле обрыва — это просто запуск нового клиента к живому серверу.
Зачем Neovim escape-time 0 и focus-events
Заголовок раздела «Зачем Neovim escape-time 0 и focus-events»Две строки в конфиге выше выглядят загадочно, но без них Neovim внутри tmux работает «с заиканиями».
escape-time. Когда ты жмёшь Esc, терминал не может сразу отличить одиночный Esc от начала escape-последовательности (стрелки, Alt-комбинации — это всё последовательности, начинающиеся с байта ESC). Поэтому tmux по умолчанию ждёт ~500 мс: вдруг за Esc придёт продолжение. Для человека в Neovim это выглядит как «Esc срабатывает с лагом в полсекунды» — невыносимо, когда выход в normal-режим ты жмёшь сто раз в минуту. set -sg escape-time 0 говорит: не жди, одиночный Esc — это Esc. Плата — теоретическая возможность проблем с Alt-последовательностями на очень медленных соединениях; на практике с нулём живут все.
focus-events. Neovim умеет реагировать на получение и потерю фокуса — например, автоматически перечитывать файлы, изменённые снаружи (:checktime), или применять тему при смене системной темы (помнишь hot-reload из главы про Omarchy). Но когда Neovim работает внутри tmux, события фокуса терминала до него не доходят — tmux их проглатывает. set -g focus-events on велит tmux транслировать события фокуса внутрь панелей, и Neovim снова видит мир.
OSC52: буфер обмена через SSH
Заголовок раздела «OSC52: буфер обмена через SSH»Осталась последняя магия. Ты по SSH на сервере, внутри tmux, внутри Neovim копируешь строку — и хочешь вставить её в браузер на своей локальной машине. Обычный системный буфер тут бессилен: сервер о нём ничего не знает. Спасает OSC52 — escape-последовательность терминала, придуманная именно для этого: программа печатает специальную последовательность с base64-содержимым, эмулятор терминала на твоей машине перехватывает её и кладёт данные в локальный буфер обмена.
Цепочка выглядит так: tmux с set-clipboard on при копировании (y в copy-mode) генерирует OSC52 → последовательность едет по SSH-каналу как обычный вывод → твой локальный терминал (Alacritty/Ghostty поддерживают OSC52) перехватывает её → строка в системном буфере. Neovim внутри tmux, кстати, тоже умеет OSC52 сам — регистр + в нём работает через ту же последовательность, если настроен clipboard-провайдер.
Типичные ошибки и грабли
Заголовок раздела «Типичные ошибки и грабли»- Одна вечная сессия на всё. Сорок безымянных окон — тот же хаос вкладок. Хорошо: сессия на проект, окна названы (
r— переименовать), мусорные окна убиваются (k). - Сплиты открываются в домашней директории. Забыт
-c "#{pane_current_path}"— и каждый новый сплит начинается с ручногоcd. Это первая строка, которую стоит добавить в конфиг. - «Esc в vim лагает» — это дефолтный
escape-time 500. Лечится одной строкой, но половина новичков списывает это на «vim тормозной». - Копирование мышью в обход tmux. Выделение мышью копирует текст одной панели с чужими границами и номерами строк. Хорошо: copy-mode (
v/y) — tmux знает границы панелей и копирует чисто. tmux kill-serverвместо убийства сессии. Убивает ВСЕ сессии со всеми процессами. Нужна одна —tmux kill-session -t имя.- Вложенный tmux без понимания. Локальный tmux, внутри SSH, на сервере ещё tmux — и префикс ловит внешний. Приём: префикс, нажатый дважды (
C-Space C-Space), отправляет префикс внутрь благодаряbind C-Space send-prefix— и бинды срабатывают на внутреннем tmux. - Конфиг без
source-file. Отредактировал.tmux.conf, а бинды не применились — конфиг читается при старте сервера. Либоtmux source-file ~/.tmux.conf, либо бинд на перечитывание.
Вопросы на собеседовании
Заголовок раздела «Вопросы на собеседовании»- Что происходит с процессами в tmux при обрыве SSH? Ничего: процессы — дети tmux-сервера, который держит их PTY. Клиентское соединение обрывается, SIGHUP до процессов не доходит. После переподключения
tmux attachвозвращает ровно то состояние, которое было. - Объясни иерархию tmux. Сервер (один на пользователя, unix-сокет) → сессии (именованные контексты) → окна (полноэкранные вкладки) → панели (сплиты внутри окна, каждая со своим PTY). Адресация:
сессия:окно.панель. - Чем tmux отличается от вкладок эмулятора терминала? Вкладки терминала живут и умирают с окном терминала и не существуют по SSH. tmux — серверный процесс: сессии переживают закрытие терминала, доступны из любого клиента, скриптуются и работают на удалённых машинах.
- Зачем
escape-time 0? Терминал отличает одиночный Esc от начала escape-последовательности только таймаутом (~500 мс по умолчанию). В модальном редакторе Esc нажимается постоянно, лаг неприемлем — ноль убирает ожидание. - Как скопировать текст с удалённого сервера в локальный буфер обмена? Через OSC52: приложение печатает escape-последовательность с base64-данными, локальный эмулятор терминала перехватывает её и кладёт в системный буфер. Требуется поддержка терминалом и
set-clipboard onв tmux. - Что такое PTY и какую роль он играет в tmux? Псевдотерминал — пара «master/slave», эмулирующая физический терминал. tmux-сервер держит master-сторону каждой панели, шелл видит slave как настоящий терминал — поэтому программы ведут себя интерактивно, а tmux может перехватывать и перерисовывать вывод.
- Как два человека могут работать в одной tmux-сессии? Два клиента подключаются к одному серверу (
tmux attachобоими): оба видят один экран и вводят в одни панели. Это простейший парный дебаг по SSH; для разных размеров окон естьtmux attachс группами сессий.
Практика
Заголовок раздела «Практика»- Разверни конфиг из главы в
~/.tmux.conf(или~/.config/tmux/tmux.conf), примениtmux source-fileи проверь каждый бинд: Alt+цифры, сплиты по Alt+Enter, ресайз. Критерий: все клавиши из конфига работают и ты можешь объяснить каждую строку. - Напиши dev.sh для pet-проекта: сессия, окно с nvim, панель с тестами, окно с lazygit. Критерий: одна команда поднимает полное рабочее место,
tmux kill-sessionсносит его без осиротевших процессов. - Сымитируй обрыв SSH. Подними VM или контейнер с SSH, подключись, запусти в tmux долгий процесс (
sleep 1000 && echo done), убей SSH-клиент (killпроцесса ssh локально). Переподключись,tmux attach— процесс жив. Это упражнение переключает tmux из категории «удобно» в категорию «доверяю». - Проверь цепочку OSC52. На удалённой машине в tmux скопируй строку в copy-mode и вставь в локальное приложение. Если не работает — найди, какое звено режет последовательность.
- Проживи день в copy-mode. Весь поиск по выводу — только префикс+
[,/,v,y. Никакого «перенаправлю в файл и открою». Критерий: к вечеруv/yжмутся бездумно.
Что почитать
Заголовок раздела «Что почитать»- tmux — репозиторий и wiki — первоисточник, включая FAQ и руководство по началу работы.
- man tmux — полный справочник: все команды, форматы
#{...}, опции. - The Tao of tmux — книга про философию мультиплексора и рабочие процессы вокруг него.
- OSC52 в спецификации xterm — раздел Operating System Commands, откуда растёт clipboard через escape-последовательности.
- Neovim в tmux: рекомендации автора — официальный FAQ, включая разбор escape-time и clipboard.