Обновить

Комментарии 77

Сам недавно столкнулся с такой проблемой, пытался решить её через CTRL+Z -> bg -> disown, правда так до конца и не понял, сработало ли, ибо забыл изменить имя выходного файла на новое, дабы проверить выходные результаты с уже имеющимися 😅

Автору спасибо за статью, всё чётко, кратко и по делу 👍🏻

да, наша "боль" – двигатель инноваций 😁

Каждый байт, который пишется в терминал, дописывается в лог-файл на диске. Не в памяти — на диске

Это значительно сократит жизнь SSD, и в некоторых устаревших дистрибутивах, где в /home/$user права r-xr-xr-x, позволит читать сессии.

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

Круто! Осталось переписать на Rust и можно юзать :)

было бы неплохо! но у меня на это времени нет увы 😎

Это всё отмазки :)

конечно, у меня нет достаточного опыта с Rust )

Это убьет главное преимущество инструмента - сборку за секунду на любом калькуляторе с POSIX и GCC. Rust потянет за собой Cargo, зависимости и бинарник на 5 мегабайт вместо 50 килобайт

Зато можно сделать бинарник один раз и копировать куда угодно.

кстати, я же тоже собираю статичные бинарники atch, написанного на С, и всё работает, и размер в килобайтах.

Да, может бинарь будет чуть-чуть крупнее, например 800кб, но будет больше внутренних проверок на ошибки и т.п.

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

Но вообще респект, вот такой простой тулзы реально бывает не хватает, чтобы без лишних фокусов были персист сессии, реаттач и скроллбек.

А я недавно открыл для себя zellij, который удобнее byobu.

да, видел недавно zellij, прикольная вещь, но слишком много наворотов для всех моих юскейсов

да, спасибо! 👍 а про byobu не знал. потом гляну

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

А как же разделение по пользователям? Или предполагается, что все коллеги ходят на сервер под одной учёткой?

конечно у каждого пользователя в системе есть свои сессии и логи, которые хранятся а ~/.cache/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'ами в том, что сделать хорошо без изменений в терминалах сложно/невозможно, а терминалы слишком консервативные.

А почему то, что нужно запустить в detach, не запустить просто через nohup? А вывод потом можно смотреть через tail -f

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

я тмукс не использую, но я знаю, что есть второй подход, помимо приложения эмулирующего терминал(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, тоже работает.

класс! можно и статик собрать под эту архитектуру. я обычно на Alpine их собираю под x86_64/aarch64, чтобы потом просто скачивать готовый бинарник когда надо.

Переизобретение dtach в 2026 году конечно похвально, но 99% сисадминов используют tmux не ради чистоты архитектуры, а потому что он есть в базовых репозиториях любого дистрибутива. Пришел на сервер, написал apt install tmux, и работаешь

А тащить свой кастомный бинарник из исходников на каждый прод-сервер это путь самурая-одиночки :)

я согласен, но не всё так просто. 😄 в моем собственном репозитории deb/rpm за годы я собрал сотни утилит, которые постоянно пополняю и обновляю. так что у меня это тот же apt/dnf install atch. но об этом я буду писать чуть позже. планирую сделать репозиторий публичным.

Правильно ли я понял, что мегабайт лога всегда проигрывается полностью при каждом подключении? То есть если у нас дохлый линк через мобилу, например, то можно и не дождаться актуального состояния?

(Просто смотрю на это с позиции человека, подключенного к машине по се́риалу на 9600)

абсолютно верно! но вы можете выбрать нужный вам размер лога в момент компиляции.

например, можно захардкодить 100кб при сборке или еще меньше.

Всегда считал, что терминал - это как раз то место, где мышь нужна по минимуму. Когда я в нём работаю, у меня обе руки на клавиатуре. Мышь иногда хватаю, чтобы скопировать что-то в терминал или из него. У вас большой упор идёт именно на решение проблем с использованием мыши. Это касается в первую очередь удобства прокрутки лога? Я уже привык нажимать 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.

спасибо большое! Работает.

Дефолт то всяко лучше 1M ставить, сектора на ссд не десятичные

так да, дефолт стоит 1М

Если нужно просто запустить что-то в фоне и уйти пользую nohup. Он не настолько продвинутый, просто позволяет процессу пережить отключение терминала и весть вывод валит в nohup.out. Зато поставляется с coreutils

да, старый дедовский метод иногда тоже неплохо помогает 😅

Искал именно этот комментарий, чтобы заплюсовать. Иначе пришлось бы писать самому про "nohup".

Интеграция 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, размер бинарника вышел бы примерно как сейчас.

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>

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

Стиль ИИ узнаваем.
Это не то. Это не это. Это вот это.

Предложения по 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 минут) - пишем на диск.
Наверное, эта опция может быть "включаеммой", потому что иногда важно хранить прям каждую строчку вывода сессии

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации