Десятилетия терминальных мультиплексеров, одна хроническая боль и маленькая тулза на C, которая наконец всё починила.

Дисклеймер. Это адаптация статьи, которую я написал на английском для medium.com. Для перевода и технических правок использовал ИИ — он сделал черновую работу, дальше доводил сам. Если что-то режет глаз — пишите в комментах, поправлю.

У каждого линуксоида есть этот момент. Ты SSH'ишься на удалённый сервак, запускаешь что-то долгое, и тут — коннект падает. Всё. Пустой терминал, и то самое чувство, когда хочется закрыть ноутбук и пойти работать барменом.

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

Глава 1: Эпоха screen

Все вокруг тогда говорили одно: «Запускай в screen'е, чувак.» Я и запустил.

GNU Screen существует с 1987 года. Он старше World Wide Web. Он стоит на каждом Unix-сервере, когда-либо существовавшем на земле — это одновременно говорит о его полезности и должно настораживать. Я набирал screen и чувствовал себя настоящим сисадмином. У меня были персистентные сессии. Я был крут.

Продолжалось это недели две.

Трещины появились быстро. Скролл мышью? Не работает. Чтобы проскроллить вверх, нужно войти в «copy mode» — Ctrl+A, потом Esc, потом стрелочками как в 1993 году, потом снова Esc. Каждый. Чёртов. Раз. Я это набирал тысячи раз, не преувеличиваю.

С цветами тоже было весело. Мой тщательно настроенный шелл-промпт нормально выглядел в обычном терминале, а внутри screen превращался в нечто из эпохи BBS. True color? Это шутка? Новые escape-последовательности, которые стали использовать мои тулзы? Screen смотрел на них и делал вид, что не заметил.

Клики мышью? Копирование мышью в сплитах? Чисто теоретические концепции.

Со временем у меня собралась коллекция твиков для .screenrc — наскребал по форумам, старым блогпостам 2009 года и ответам на Stack Overflow в духе «не знаю, но попробуй». Мой конфиг превратился в музей карго-культа.

И тут появился tmux с обещанием спасти меня.

Глава 2: Эпоха tmux (или: другие проблемы, те же страдания)

tmux объективно лучше screen почти по всем параметрам. Чище архитектура, адекватнее дефолты, вертикальные и горизонтальные сплиты, нормальный оконный менеджер. Сообщество живое. Целая экосистема плагинов. Куча людей им клянётся, и я честно понимаю почему.

Я юзал tmux годами. У меня до сих пор есть мнения о tmux, которые я готов отстаивать в любой компании.

Но с мышью никогда не было порядка. Добавляешь set -g mouse on в конфиг, чувствуешь себя победителем минут пять, а потом оказывается, что скролл мышью в пейне скроллит внутренний буффер tmux, а не программу, которая там реально запущена. Все интерактивные инструменты — fzf, vim, htop — вели себя по-разному внутри и снаружи tmux. Иногда немного по-другому, иногда совсем непредсказуемо.

Причина архитектурная: tmux реализует собственный эмулятор терминала. Когда программа что-то выводит, tmux перехватывает вывод, парсит, перекодирует и отдаёт дальше. Без этого он не может вести собственный скроллбек и управлять пейнами. Для обычного текста нормально, но mouse reporting — это тоже escape-последовательности, и когда tmux пропускает их через свой виртуальный терминал, начинается магия. Причём плохая магия. Мышь либо вообще переставала работать, либо требовала каких-то специфических настроек terminfo, которые нужно было находить и помнить на каждой машине.

Со скроллбеком отдельная история. Буфер скроллбека в tmux живёт в памяти процесса. tmux упал — буфер пропал. Машину ребутнули — очевидно пропал. Можно было включить логирование плагином или обернуть всё в script, но это костыли со своими косяками.

Про конфиг вообще молчу. В tmux.conf порядок опций имеет значение, некоторые настройки должны идти раньше других, опечатка тихо сломает поведение без единого сообщения об ошибке. Я убил на отладку tmux.conf больше времени, чем хочу признавать. Вокруг этого целая индустрия: блогпосты, ютуб-туториалы, стартовые дотфайлы — всё посвящено одному вопросу: «как заставить tmux вести себя как нормальный терминал».

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

Глава 3: Блуждания в поисках — abduco и dvtm

В какой-то момент между годами сидения на tmux я начал искать альтернативы. Наткнулся на пару тулов от Marc André Tanner: abduco и dvtm.

Философия — чистый Unix: делай одно дело хорошо. Abduco занимается только управлением сессиями (detach, reattach, и всё). DVTM занимается только оконным менеджментом в терминале (тайловые пейны). Хочешь и то, и другое — комбинируй. Нужна только персистентность без сплитов — юзай просто abduco.

Abduco чистый, современный, лёгкий. По сути — это то, чем мог бы быть dtach, если бы его написали сегодня. Вдвоём они дают примерно тот же фичсет, что tmux, но без половины головной боли.

Но тут я понял кое-что про себя: сплиты мне на самом деле не нужны. Я юзал пейны в tmux по привычке, а не по необходимости. Мой реальный воркфлоу: одна сессия, один процесс, зашёл по SSH, проверил, вышел. Оконный менеджер я таскал с собой только потому, что он шёл в комплекте с сессионным менеджером.

Так я перешёл на dtach.

Глава 4: dtach и тот самый момент

dtach — это концепция «просто отдетачиться от терминала» в чистейшем виде. Маленькая программа на C, написанная в начале 2000-х Недом Криглером. Делает ровно одно: позволяет отцепить запущенную программу от терминала и переподключиться позже. Никакого мультиплексинга, никакого скроллбека, никаких плагинов, никакого конфига, никакого статусбара.

Запускаешь dtach -c /tmp/mysession myprogram, жмёшь Ctrl+\ чтобы отцепиться, запускаешь dtach -a /tmp/mysession чтобы переподключиться. Весь API.

Меня поразила прозрачность. dtach не пытается интерпретировать вывод. Он не реализует виртуальный терминал. Он просто пробрасывает байты. Мышь работала. Цвета работали. Всё работало — потому что dtach просто не мешал.

Вот чего мне не хватало всё это время.

Но пробелы никуда не делись. Нет персистентного лога — ушёл из сессии, процесс завершился до твоего возвращения, вывод пропал. Нет команды list чтобы посмотреть, какие сессии вообще есть. Именование сокетов полностью ручное. Нет способа послать ввод в работающую сессию снаружи. Мелочи, но их набиралось.

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

Так получился atch.

Глава 5: atch — что мне было нужно на самом деле

atch — это то, что получается, когда берёшь прозрачность dtach и добавляешь всё, чего в нём не хватало.

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

Что это значит на практике:

Мышь просто работает. Не «работает если добавишь три строчки в конфиг и помолишься». Просто работает. Сразу, всегда. Потому что atch никогда не перехватывал mouse-последовательности.

Скролл просто работает. Программы с alternate screen (vim, htop, fzf — да всё интерактивное) ведут себя внутри atch-сессии точно так же, как снаружи. У atch нет мнения насчёт alternate screen. Всем занимается твой терминал, как обычно.

True color, sixel, kitty graphics, OSC-последовательности — всё проходит насквозь. Переменная $TERM не трогается. Программа видит ровно тот же терминал, что и в обычном шелле.

Конфигурировать нечего. Нет ~/.atchrc. Нет хоткея, который надо помнить (только символ детача — Ctrl+\, который можно поменять или вообще отключить). Нет prefix key. Нет системы плагинов. Нет set -g. Тул делает свою работу и не мешает.

Фича, про которую я не знал, что она мне нужна

Все существующие сессионные менеджеры — screen, tmux, abduco, dtach — хранят историю скроллбека в памяти. Это значит: процесс завершился — история пропала. Машина ребутнулась — пропала. Процесс крашнулся — пропала. Восстановления нет.

atch делает иначе. Каждый байт, который пишется в терминал, дописывается в лог-файл на диске. Не в памяти — на диске, в ~/.cache/atch/. Файл всегда есть, всегда актуальный.

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

Конкретный сценарий: запустил билд в atch-сессии, машину ребутнула ops-команда (или kernel update, или просто так), ты SSH'ишься обратно, делаешь atch work — и видишь весь вывод билда, всё что он напечатал до ребута, а потом свежий шелл.

Это не плагин. Это не опция в конфиге. Это поведение по умолчанию.

Лог ограничен 1 МБ по умолчанию (меняется при компиляции), есть atch clear если нужен чистый старт. В остальных случаях он просто лежит на диске, тихо, незаметно, и спасает тебя в нужный момент.

Как пользоваться

Команды намеренно простые. Достаточно помнить одну:

# Зайти в сессию "work" (создаст если не существует)
atch work

# Запустить конкретную команду в именованной сессии
atch new build -- make -j4

# Детачнуться: Ctrl+\

# Посмотреть все сессии
atch list

# Запустить в фоне — atch сразу завершится
atch start daemon myserver

# Послать ввод в работающую сессию
printf 'ls -la\n' | atch push work

# Убить сессию
atch kill work

# Почистить историю
atch clear work

Это 90% того, что используешь в повседневке. Полный список команд тоже маленький — намеренно.

Сессии — просто именованные сокеты в ~/.cache/atch/. Если в имени есть / — используется как путь в ФС напрямую. Никакой магии, никакого глобального демона, за которым надо следить.

Если пишешь скрипт и надо проверить, находишься ли внутри atch-сессии — есть atch current. Возвращает 0 и имя сессии если внутри, 1 если нет. Удобно для промпта и автоматизации.

CI/CD и AI-агенты

Вот где становится по-настоящему интересно для современных воркфлоу.

Комбинация прозрачного проброса + персистентных логов на диске + push stdin + детачнутый запуск + возможность детектить сессии из скриптов — это мощный примитив для автоматизации.

CI/CD пайплайны. Запускаешь долгий деплой через atch start deploy ./deploy.sh из webhook-хэндлера, хэндлер сразу отвечает, не ждёт. Потом проверяешь деплой через atch attach deploy из любого SSH-соединения. Полный вывод всегда на месте: деплой завершился до твоего прихода — лог на диске; ещё работает — смотришь в реальном времени. В любом случае полная картина.

AI-агенты. Именно для этого я сейчас использую atch больше всего. Долгоиграющие AI-процессы — те, что работают часами, делают API-запросы, обрабатывают данные, пишут файлы — за ними реально неудобно следить. Нужно мочь отключиться и вернуться. Нужно видеть, что агент делал пока тебя не было. Нужно инжектить ввод когда хочешь дать новые инструкции. printf 'continue with the next task\n' | atch push agent — просто работает. Персистентный лог означает полный аудит-трейл всего, что агент напечатал, выживает после любого краша или ребута. Когда оркестрируешь несколько агентских сессий, atch list даёт чёткую картину что сейчас работает, и к любой сессии можно моментально подключиться.

Серверные процессы. Запускаешь сервер в atch-сессии, даёшь имя сессии коллеге, он аттачится и смотрит вывод. Несколько клиентов могут быть подключены к одной сессии одновременно — все видят один стрим.

Разбор полётов после краша. Машина ребутнулась. Сессия умерла. Лог на диске. Не надо восстанавливать картину по разрозненным записям в syslog и надеяться, что кто-то смотрел.

Сравнение с конкурентами

Без лишних слов:

vs. tmux: У atch нет сплита окон, статусбара, плагинов, никакого оконного менеджмента. Если твой воркфлоу построен вокруг лейаутов tmux — atch не замена. Но если тебе нужны персистентные сессии, которые не ломают терминал и не требуют конфига — atch намного проще и просто работает.

vs. screen: Screen — монолит с десятилетиями накопленной сложности и минимальной активной разработкой. atch — несколько тысяч строк читаемого C. Мышь в atch работает. В Screen — фундаментально нет.

vs. abduco: Abduco хорош и разделяет ту же философию прозрачности. atch добавляет персистентную историю на диске (abduco хранит историю только в памяти — умер процесс, история пропала), команду push для инжекта stdin извне сессии, встроенный листинг сессий и чуть более дружелюбный интерфейс команд.

vs. dtach: atch — прямой духовный наследник dtach, с историей на диске, листингом сессий, push stdin и рядом QoL улучшений. Обратно совместим с флаговым синтаксисом dtach, старые скрипты не ломаются.

Честная часть

atch не пытается заменить tmux тем, кто активно использует его оконный менеджмент. Если твой воркфлоу — это постоянные сплиты, прыжки между пейнами и плагины, в atch ничего этого нет.

Но если ты хоть раз гуглил «tmux mouse scrolling not working» в одиннадцать вечера, если у тебя в tmux.conf есть строчки, которые ты копируешь с машины на машину не до конца понимая зачем, если хоть раз терял историю сессии в самый неподходящий момент — возможно, это то, что ты искал.

Код достаточно маленький, чтобы прочитать его за вечер (без шуток, это реально полезно). Собирается через make, зависимостей нет кроме компилятора C и POSIX-окружения. После установки конфигурировать нечего.

Тул делает одно: держит твою программу запущенной и связанной с терминалом, даже когда ты не подключён. Каждый байт — в лог. Каждая сессия — доступна по имени. Мышь, скролл, цвета — дело твоего терминала, не atch. Всё остальное не мешает.


Выложил на GitHub: https://github.com/mobydeck/atch. Собирается за секунды.
Запусти atch work — за пять минут поймёшь, подходит тебе или нет.

Сам использую ежедневно уже какое-то время. Ни разу не вспомнил про настройку мыши в tmux.

И еще бонус... Почти год исполняется другим моим инструментам suex и sush, о которых я недавно писал на Хабре, и есть репозиторий на Гитхабе https://github.com/mobydeck/suex