
Комментарии 77
Сам недавно столкнулся с такой проблемой, пытался решить её через CTRL+Z -> bg -> disown, правда так до конца и не понял, сработало ли, ибо забыл изменить имя выходного файла на новое, дабы проверить выходные результаты с уже имеющимися 😅
Автору спасибо за статью, всё чётко, кратко и по делу 👍🏻
Каждый байт, который пишется в терминал, дописывается в лог-файл на диске. Не в памяти — на диске
Это значительно сократит жизнь SSD, и в некоторых устаревших дистрибутивах, где в /home/$user права r-xr-xr-x, позволит читать сессии.
Круто! Осталось переписать на Rust и можно юзать :)
было бы неплохо! но у меня на это времени нет увы 😎
Это убьет главное преимущество инструмента - сборку за секунду на любом калькуляторе с POSIX и GCC. Rust потянет за собой Cargo, зависимости и бинарник на 5 мегабайт вместо 50 килобайт
Странно что как будто никто не знает про byobu. Это враппер поверх tmux или screen с готовыми конфигами для людей, с ним можно сносно жить и не знать ужасов настройки всего этого легаси.
Но вообще респект, вот такой простой тулзы реально бывает не хватает, чтобы без лишних фокусов были персист сессии, реаттач и скроллбек.
Запускаешь сервер в atch-сессии, даёшь имя сессии коллеге, он аттачится и смотрит вывод. Несколько клиентов могут быть подключены к одной сессии одновременно — все видят один стрим.
А как же разделение по пользователям? Или предполагается, что все коллеги ходят на сервер под одной учёткой?
отличная вещь, утащил себе
один мелкий баг - если я внутри сессии делаю
```
# atch kill test1
[1] 1662212 terminated
# atch current
test1
<Crtl+\>
atch: session 'test1' detached
# atch list
test1 since 2m 35s ago
# atch kill test1
./atch: session 'test1' did not stop
```
Со всеми этими новыми инструментами есть одна проблема: их нет в репах системы, которая запущена на сервере.
Мой подход, конешно, многие сочтут примитивным, но я просто использую табы в терминале и в каждом запускаю сессию ssh (SSH ControlMaster). То, что нужно запустить в deattach запускаю в tmux. Никаких проблем с копированием текста туда-обратно, скроллом и т.д. Сплиты я все равно никогда не использовал, мне табов хватает.
Насколько я понимаю, основная проблема со всеми этим tmux'ами в том, что сделать хорошо без изменений в терминалах сложно/невозможно, а терминалы слишком консервативные.
я тмукс не использую, но я знаю, что есть второй подход, помимо приложения эмулирующего терминал(XTERM-pty или VT-pty), там пошустрее попроще донастроить окружение, грубо говоря, если это не tty сессия, то проще просто визуально настроить окружение в текстовом оформлении ), к слову 2 таких примера я себе реализовал, 1 - это просто терминал на java(pty), а второе(та самая текстовая обвязка приложения на окружение) это текстовое окружение визуальное с визуальным поведением терминала(без сессии) тоже на java - оба опыта просто великолепны, но я с тмукс даже не знаком ), в планах, замикшировать эти 2 решения в одно ) обвязать буферами по вызову ), тоесть представьте, можно иметь такое приложение, где можно вызывать своё текстовое пространство иметь клипбоард, и иметь хоткей на терминал стандартный(VT-pty с сессией)
тоесть (GUI text style)текстовая умная оболочка, которая имеет свои утилиты связывает разрозненные команды системы, и где есть свой емулятор терминала стандартный
Я тоже никогда не понимал этого культа screen и подобного (может, потому что далёк от админства). До старта иксов доступна текстовая виртуальная консоль Alt-F[1..n]. В иксах можно запустить табовый эмулятор терминала (их тьма, например tilix), настроить вывод на каждый таб последней исполненной команды для удобства ориентирования, переключаться Ctrl-Tab и Alt-[1..n] и всё. Не нужны никакие специальные комбинации, мышь и прочее. Да, по ресурсам, возможно, несколько затратнее, но на фоне Electron-приложений...
Иногда бывает, что вся разработка и любая работа с данными должны быть на специальном сервере. А на вашем компе/ноутбуке только браузер, терминал с ssh, ну и vs code.
"Культ screen и подобного" возникает не когда ты работаешь у себя на машине, а когда зашел по ssh на сервер и запускаешь там что-то продолжительное. В таком случае "подобное" позволяет запустить и уйти/оторваться, а потом вернуться и посмотреть, как оно там и чем дело кончилось. А не так, что ты зашел, запустил fsck какой-нибудь, а у тебя соединение с сервером отвалилось.
Спасибо. Попробовал на древнем Raspberry Pi Model B (Pidora18, репозиторий уже не работает), собрался после удаления -static. Работает нормально.
Так же проверил на Ubuntu 24, тоже работает.
Переизобретение dtach в 2026 году конечно похвально, но 99% сисадминов используют tmux не ради чистоты архитектуры, а потому что он есть в базовых репозиториях любого дистрибутива. Пришел на сервер, написал apt install tmux, и работаешь
А тащить свой кастомный бинарник из исходников на каждый прод-сервер это путь самурая-одиночки :)
Правильно ли я понял, что мегабайт лога всегда проигрывается полностью при каждом подключении? То есть если у нас дохлый линк через мобилу, например, то можно и не дождаться актуального состояния?
(Просто смотрю на это с позиции человека, подключенного к машине по се́риалу на 9600)
Всегда считал, что терминал - это как раз то место, где мышь нужна по минимуму. Когда я в нём работаю, у меня обе руки на клавиатуре. Мышь иногда хватаю, чтобы скопировать что-то в терминал или из него. У вас большой упор идёт именно на решение проблем с использованием мыши. Это касается в первую очередь удобства прокрутки лога? Я уже привык нажимать C-A+{ для прокрутки, но чаще это работа с файлом в vim и, соответственно, достаточно быстрая навигация клавиатурой.
В копилку к прочим аналогам - shell-pool/shpool. Идея аналогичная - просто сохранение сессий, без окон и пейнов. Пользовался какое-то время, потом перешёл на zellij.
И оно на расте, раз тут в комментах спрашивают.
спасибо огромное! Узнал про atch и abduco. Работает (atch) действительно просто и удобно. Да, tmux надоедает, особенно после паузы в использовании, когда не хочется лезть и настраивать.
Вопрос. Если не нужен вывод истории в файл, можно ли его отключить? Пока по документации такой опции не смог найти.
отключить нельзя, но я могу добавить, если объясните как вам нужно отключать. либо по флагу при запуске, либо во время компиляции функционал отключить.
как будто флаг лучше. Например "-C" disable disk cache.
Лучше сделать возможность указывать размер кэша. Оставить мегабайт по-умолчанию.-C 0 # Отключить
-С 128k # 128Кб
-С 4m # 4Мб
Ну задумку вроде объяснил.
добавил! 😎 вот пример:
atch -C 4m new mysession # 4 MB cap
atch -C 128k start daemon # 128 KB cap
atch -C 0 start daemon # no log at all-C <size>Set the on-disk log cap for the session being created. Accepts a bare number (bytes), or a number with k/K (KiB) or m/M (MiB) suffix. 0 disables the log entirely. Default: 1m.
Если нужно просто запустить что-то в фоне и уйти пользую nohup. Он не настолько продвинутый, просто позволяет процессу пережить отключение терминала и весть вывод валит в nohup.out. Зато поставляется с coreutils
Интеграция tmux в iterm: просто существует
Сейчас этого стало меньше, но раньше прям боль была
Мне надо работать с многими машинами по ssh, открыл коннекты, сидишь и тут раз, обрыв. Переподключился VPN, прыгнул с Wi-Fi на провод или подключился по мобилке...
И все, все сессии оборвались.
screen/tmux решал это тем, что он поднимался где-то на сервере (даже на сервере за бастионом) и надо было восстановить всего одно подключение (tmux вообще сам)
А atch + табы этого не решает
как я сейчас использую atch, это очень нестандартно.
я использую кастомный ssh, который инжектит на хост atch, если он там не установлен, и дальше создается atch сессия если в логине пользователя был префикс % – это у меня флаг для персистентной сессии. моя ssh команда выглядит как ssh %user@host.com и я попадаю в сессию atch.
иными словами – утилита разоработана под мои собственные нужды для ssh соединений.
но использовать atch можно по-разному 😎
https://github.com/neurosnap/zmx видели?
Недавно появился и как я понимаю тоже с намерением сделать как dtach, но лучше.
да, видели. проблема в том, что он весит 10MB, написан на zig.
atch и dtach это чистый C и размер бинарника в килобайтиках 😎
-rwxr-xr-x 1 dev dev 9.8M Feb 23 13:56 zmx
-rwxr-xr-x 1 dev dev 130K Mar 2 23:02 atchПри ReleaseSmall zmx меньше мегабайта:
$ time zig build -Doptimize=ReleaseSmall
real 0m56,688s
user 3m11,146s
sys 0m22,207s
$ ls -l zig-out/bin/
total 884
-rwxrwxr-x 1 self self 901600 2026-03-07 18:38 zmx
Да и в вашем выводе 130K - это уже не килобайтики, а весомые сотни килобайт.
Да, собирается не быстро (atch ещё не пробовал собирать, так что не могу сравнить).
Ок, atch поменьше, зато у zmx язык моднее-безопаснее и звёзд у репы на порядок больше. Но все эти критерии мало что говорят и выбора я пока для себя не сделал.
Я не объяснил, почему моя утилита написана именно на C.
Более 10 лет назад, когда я начал использовать Go в работе, я сразу подумал, что это современная замена C.
Но прошло время, и я начал возвращаться к C, переписывая небольшие утилиты на нём. Причин несколько.
Asm и C были созданы в эпоху малых хранилищ данных (или их отсутствия), поэтому бинарники были максимально оптимизированы. В наше время несколько лишних мегабайт почти ничего не значит. Но когда утилит становится много и нужно быстро развернуть десяток, это может вылиться в сотни мегабайт, и установка займёт минуты вместо секунд.
Когда каждый день работаешь с десятками недолгоживущих Linux-систем, всё это создаёт бесполезный трафик в несколько гигабайт.
Я бы даже atch переписал на Asm, если бы мог. 😅
Короче говоря вам важно оптимизировать под размер, если я правильно понял.
Это однако не означает, что Zig вам не подходит. Я не программист, но из того что я читал про него, это такой современный C, который в том числе прозрачно с C взаимодействует и позволяет собирать его проекты. Как я понимаю, zmx такой большой потому что работает через libghostty-vt, внутри всё-таки эмулируя терминал. А ему для этого дофига всего надо, например парсить юникод. Вот отчёт клода с анализом:
Что вносит основной вклад в размер (881Ki ReleaseSmall)
.text — 538Ki (61%)
┌──────────────────────────────┬─────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Источник │ ~Размер │ Причина │
├──────────────────────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ghostty: terminal.* │ ~170Ki │ Terminal emulator: PageList, Screen, OSC parser, Stream, formatter — весь ghostty-vt │
├──────────────────────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ log.scoped инстанциации │ ~125Ki │ 127Ki из одного ghostty terminal/stream — каждый log.warn("...", .{}) с уникальной format-строкой = отдельная скомпилированная функция (Zig comptime) │
├──────────────────────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ std.debug (panic/stacktrace) │ ~70Ki │ DWARF reader (debug.Dwarf.ElfModule.load = 26Ki), stack unwinder, panic machinery. Тянется из-за того что binary linked с libcxx │
├──────────────────────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ zmx: main.* │ ~60Ki │ Собственный код daemon, attach, list │
├──────────────────────────────┼─────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ std (fmt, collections) │ ~65Ki │ std.fmt, ArrayHashMap, etc. │
└──────────────────────────────┴─────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
.rodata — 227Ki (26%)
┌───────────────────────────────┬────────┬─────────────────────────────────────────────────────┐
│ Источник │ Размер │ │
├───────────────────────────────┼────────┼─────────────────────────────────────────────────────┤
│ props.Tables.stage2 (Unicode) │ 61Ki │ Unicode property lookup table (ghostty-vt) │
├───────────────────────────────┼────────┼─────────────────────────────────────────────────────┤
│ props.Tables.stage1 (Unicode) │ 16Ki │ Второй уровень таблицы │
├───────────────────────────────┼────────┼─────────────────────────────────────────────────────┤
│ simdutf lookup tables │ ~30Ki │ UTF-8/UTF-16 SIMD conversion tables │
├───────────────────────────────┼────────┼─────────────────────────────────────────────────────┤
│ format strings & strings │ ~120Ki │ Строковые литералы (в т.ч. от 155 log-инстанциаций) │
└───────────────────────────────┴────────┴─────────────────────────────────────────────────────┘
.eh_frame — 53Ki (6%)
C++ exception unwinding frames. Приходят от linkage с libcxxabi + libunwind (которые ghostty-vt тянет из-за SIMD C++ кода). Этот оверхед есть всегда при смешивании Zig + C++.
.data.rel.ro — 37Ki (4%)
Таблицы виртуальных функций C++ (vtables) из libcxxabi. Нужны для C++ exceptions / RTTI в simd/*.cpp.
---
Итоговая картина зависимостей
ghostty-vt (~65% размера)
├── terminal/* Zig code ~170Ki .text (VT emulator core)
├── unicode property tables ~80Ki .rodata (Unicode lookup tables)
├── log.scoped инстанциации ~125Ki .text (ghostty's logging в stream.zig)
└── simd/ C++ (simdutf, highway) ~40Ki .text + C++ overhead:
├── .eh_frame 53Ki (C++ unwind frames)
└── .data.rel.ro 37Ki (C++ vtables/RTTI)
std.debug / panic machinery ~70Ki .text (DWARF reader для stacktrace)
cxa_demangle (libcxxabi) ~132Ki VM (C++ name demangler — для panic demangling)
zmx own code ~60Ki .text
Главные выводы
1. Доминирует ghostty-vt: ~65% бинарника. Это цена за использование полноценного VT эмулятора.
2. log.scoped(.stream).warn — 127Ki сюрприз. В ghostty terminal/stream.zig множество log-вызовов, каждый с уникальной строкой → Zig генерирует отдельную функцию на каждый. Это называется "log bloat" от comptime-инстанциации.
3. C++ overhead (libcxxabi + libunwind + .eh_frame + .data.rel.ro) = ~280Ki — неизбежная стоимость использования simd/*.cpp из ghostty, который тянет C++ runtime.
4. std.debug = ~70Ki — DWARF reader для красивых panic stacktrace. Без него было бы меньше.
Если бы вы писали atch на zig, размер бинарника вышел бы примерно как сейчас.
Я вот такой тул написал для opencode недавно: https://github.com/konovalov-nk/remote-run/
И да, там куча проблем до сих пор 🤣
Это было временное решение чтоб позволить агенту логиниться в proxmox/hetzner и другие машины, пока я мог бы наблюдать какие команды он запускает
Тут используется tmate, который построен поверх tmux. Проблема точно такая же: логиниться одной командой к серверу `rtmate init` (список берётся из .ssh/config), а далее набираешь rtmate attach <сервер опционально>
А агент использует rtmate run (пользователь не видит вывод), либо rtmate send / read команды (буквально отправляет keypress через сокет, т.е. буквально набирает команды от пользователя и читает текущий буфер терминала)
В идеале нужно написать программку которая работает просто поверх ssh отправляет/принимает байты по какому-нибудь протоколу, например RPC. Кидаю вот старый вариант который я рассматривал, сейчас его бы взял:
-> Вариант 4 (реально инженерно-правильный): SSH как транспорт + RPC протокол
Ты поднимаешь на удалёнке маленький “агент-демон” (например, на python/go), который:
принимает команды (stdin/json)
запускает их (pty или без)
стримит stdout/stderr назад
и параллельно может писать в tmux для видимости
Это уже “настоящий” дизайн: один источник истины — процесс и его потоки, а tmux — просто “вьювер”.
Плюсы:
идеально для автоматизации
можно делать интерактив, отмену, таймауты, статусы
Минусы:
это уже проект, не трюк на 20 строк

В принципе работает, но восстановление из лога портит цвета похоже. HTOP стал тотально желтым после atch attach <session>
Стиль ИИ узнаваем.
Это не то. Это не это. Это вот это.
Предложения по 2-3 слова.
Абзацы по 1-3 предложения. Для глупых читателей. Это не для умных.
Пафос где не надо.
И все такое) Такого можно избежать с помощью соответствующих промптов, и кажется, об этом была недавно статья на Хабре.
Есть ли возможность ограничить реплей при подключении последними N командами? Если нет, прошу добавить. Функция должна отличаться от флага -C: сам лог должен быть, но каждый раз вываливать его не надо. Кейс: зайти глянуть закончилось или нет. Полный лог посмотрю, если закончилось, иначе последние N записей (напр. понять прогресс или увидеть ошибки).
👍 думаю ваша идея имеет смысл. могу добавить на днях.
я вот вижу что можно сделать подобие tail:
atch tail work # print last 10 lines of session 'work'
atch tail -f work # last 10 lines and follow
atch tail -f -n 20 work # last 20 lines and followчто думаете?
☝️ добавил в версии 0.4
Спасибо! Я вообще предполагал, что это будет параметр attach, например atch my -t 5, но как у вас мне даже больше нравится, поскольку позволяет грепать вывод. Я теперь думаю, как бы искать по полному логу. Можно развить tail, чтобы он выдавал полный лог:
atch tail -a my | grep xxx
Или можно сделать log отдельной командой а --tail — его опцией:
atch log -t 10 my
Есть ещё небольшие баги, но, похоже, нам пора в Issues перемещаться.

Спасибо, пользуюсь. Единственно не понятно как избавиться от от staled сессий в atch list и законченных сессий в atch list -a
[user@host:~]$ atch list
hf-download since 1d 5h 42m 0s ago [stale]
[user@host:~]$ atch kill hf-download
atch: session 'hf-download' is not running
[user@host:~]$ atch clear hf-download
atch: session 'hf-download' log cleared
[user@host:~]$ atch list
hf-download since 1d 5h 42m 20s ago [stale]
[user@host:~]$ atch list -a
hf-download since 1d 5h 37m 25s ago [stale]
hf-download-qwen-coder-next-q4 since 33m 34s ago [exited]
qwen3-coder-next-q8 since 4s ago [exited]
[user@host:~]$ atch clear qwen3-coder-next-q8
atch: session 'qwen3-coder-next-q8' log cleared
[user@host:~]$ atch list -a
hf-download since 1d 5h 37m 25s ago [stale]
hf-download-qwen-coder-next-q4 since 33m 34s ago [exited]
qwen3-coder-next-q8 since 4s ago [exited]
на такие сессии нет встроенной команды, но можно удалить сокет stale сессии:rm ~/.cache/atch/hf-download
по сути stale сессия - это неактивная сессия у которой остался файл сокета на диске, потому что он не был закрыт
добавил команду для удобства
atch rm mysession # remove one stale or exited session
atch rm -a # remove all stale and exited sessions at onceСпасибо за реально полезную утилитку, да и за статью с погружением в принципы работы. Из предложений (видел выше кто-то писал про SSD диски и их нелюбовь к частым перезаписям) - можно пойти по условно пути редиса, когда храним в памяти, но в какой-то момент (раз в минуту/5 минут) - пишем на диск.
Наверное, эта опция может быть "включаеммой", потому что иногда важно хранить прям каждую строчку вывода сессии
Бросил tmux и написал свой инструмент