# GNU Screen: сессии, которые переживают обрыв SSH

Индекс LLMS: [llms.txt](/llms.txt)

---

Долгий `apt upgrade`, миграция, сборка — и ноутбук ушёл в сон. SSH рвётся, процесс получает SIGHUP и умирает. **GNU Screen** держит терминал на сервере: отключился, подключился снова, работа на месте.

Это не замена `nohup` и не «ещё один SSH». Это мультиплексор: именованные сессии, несколько окон внутри одной, общая сессия на двоих. На старых RHEL/Debian `screen` часто уже стоит, когда `tmux` ещё нет.

## Минимальный цикл

На сервере:

```bash
screen -S deploy
```

Дальше обычный шелл. Чтобы уйти, не убивая процессы: `Ctrl-a`, отпустить, затем `d`. Сессия остаётся **Detached**.

Список и возврат:

```bash
screen -ls
screen -r deploy
```

Если сессия всё ещё **Attached** на другом терминале (забыли отсоединиться на работе):

```bash
screen -d -r deploy
```

Сначала отсоединит там, потом подключит здесь.

> [!NOTE]
> Префикс — `Ctrl-a`. Клавиши **не** жмут вместе: `Ctrl-a`, отпустить, затем буква. `Ctrl-a a` отправляет настоящий Ctrl-a в программу внутри (это нужно `emacs` и редко `bash`).

## Установка

```bash
# Debian / Ubuntu
sudo apt install screen

# RHEL / Alma / Rocky
sudo dnf install screen

# Alpine
sudo apk add screen
```

Проверка: `screen -v`. Вертикальный сплит (`Ctrl-a |`) есть с GNU Screen 4.1; на очень старых ящиках его нет.

## Ключи командной строки

| Ключ | Что делает | Пример |
| --- | --- | --- |
| `-S имя` | создать или обратиться по имени | `screen -S logs` |
| `-ls` / `-list` | список сессий | `screen -ls` |
| `-r [имя]` | подключиться к отсоединённой | `screen -r logs` |
| `-d -r имя` | отсоединить там, подключить здесь | `screen -d -r logs` |
| `-R` | подключить существующую или создать новую | `screen -R logs` |
| `-x [имя]` | подключиться **не** отсоединяя другую сторону | `screen -x logs` |
| `-dmS имя команда` | старт в фоне, сразу Detached | `screen -dmS backup /opt/backup.sh` |
| `-L` | писать лог `screenlog.N` в текущем каталоге | `screen -L -S migrate` |
| `-Logfile файл` | путь к логу (вместе с `-L`) | `screen -L -Logfile /tmp/migrate.log -S migrate` |
| `-X команда` | послать команду в уже живую сессию | `screen -S logs -X stuff 'tail -f /var/log/nginx/error.log\n'` |
| `-wipe` | убрать мёртвые сокеты из списка | `screen -wipe` |

Имя в `-S` — то, что потом ищете в `screen -ls`. Без имени получите что-то вроде `12345.pts-0.hostname` — вспоминать неудобно.

Типичный фон для скрипта, который должен пережить разрыв SSH:

```bash
screen -dmS pg-dump pg_dump -Fc -f /backup/app.dump app
screen -ls
# позже
screen -r pg-dump
```

Когда команда завершится, сессия обычно исчезает. Нужен шелл после скрипта:

```bash
screen -dmS build bash -lc 'make -j"$(nproc)"; exec bash'
```

## Окна внутри сессии

Одна сессия — несколько окон: сборка, логи, второй шелл. Переключение без нового SSH.

| Клавиша | Действие |
| --- | --- |
| `Ctrl-a c` | новое окно |
| `Ctrl-a n` / `Ctrl-a p` | следующее / предыдущее |
| `Ctrl-a 0` … `Ctrl-a 9` | окно по номеру |
| `Ctrl-a "` | список окон, выбор стрелками |
| `Ctrl-a '` | перейти по номеру или имени |
| `Ctrl-a A` | переименовать текущее окно |
| `Ctrl-a Ctrl-a` | предыдущее активное окно |
| `Ctrl-a k` | закрыть окно (спросит подтверждение) |
| `Ctrl-a \` | убить все окна и выйти из screen |

Имена окон помогают, когда их больше двух: `Ctrl-a A` → `nginx-log`.

## Сессия, копирование, сплит

| Клавиша | Действие |
| --- | --- |
| `Ctrl-a d` | отсоединиться, процессы живут |
| `Ctrl-a D D` | «жёсткое» отсоединение (и с других дисплеев) |
| `Ctrl-a ?` | справка по клавишам |
| `Ctrl-a :` | командная строка screen (`quit`, `sessionname`, …) |
| `Ctrl-a a` | послать Ctrl-a внутрь окна |
| `Ctrl-a [` | режим копирования / прокрутка буфера |
| `Ctrl-a ]` | вставить буфер обмена screen |
| `Ctrl-a Esc` | то же, что `Ctrl-a [` |
| `Ctrl-a S` | сплит по горизонтали (регион сверху/снизу) |
| `Ctrl-a \|` | сплит по вертикали |
| `Ctrl-a Tab` | фокус в соседний регион |
| `Ctrl-a X` | закрыть текущий регион (окно не убивается) |
| `Ctrl-a Q` | оставить только текущий регион |
| `Ctrl-a H` | вкл/выкл лог `screenlog.N` |
| `Ctrl-a M` | следить за активностью в окне (колокольчик) |
| `Ctrl-a x` | залочить сессию (пароль пользователя) |

Прокрутка: `Ctrl-a [`, затем стрелки или `PageUp` / `PageDown`. Выделение: `Пробел` — начало, стрелки, `Enter` — в буфер screen. Выход из режима — `Esc`. Вставка — `Ctrl-a ]`. Это **не** системный clipboard: буфер живёт внутри screen.

После сплита новый регион пустой, пока в него не переключитесь (`Ctrl-a Tab`) и не выберете окно (`Ctrl-a n` или `Ctrl-a "`).

## Сценарии с сервера

**Долгий деплой.** Именованная сессия, лог на диск, отключились и пошли домой:

```bash
screen -L -Logfile ~/migrate.log -S migrate
# внутри: ansible-playbook -i prod site.yml
# Ctrl-a d
```

Утром: `screen -r migrate` или просто `tail -f ~/migrate.log`.

**Несколько задач в одном SSH.** Сессия `ops`, окна `build`, `journal`, `sql`:

```bash
screen -S ops
# Ctrl-a c  — ещё окно
# Ctrl-a A  — имя
```

**Парный просмотр.** Оба под одним пользователем:

```bash
# первый
screen -S incident
# второй, не выкидывая первого
screen -x incident
```

Оба видят один и тот же терминал. Для инцидента это быстрее, чем «скинь вывод».

**Команда с ноутбука в уже открытую сессию** — без входа руками:

```bash
screen -S ops -X screen bash
screen -S ops -X stuff 'systemctl status nginx\n'
```

`-X screen` создаёт окно, `stuff` печатает в него символы. `\n` — как Enter.

## Короткий `.screenrc`

По умолчанию scrollback маленький, баннер при старте мешает, имён окон не видно. В `~/.screenrc`:

```screen
startup_message off
vbell off
defscrollback 20000
shell -$SHELL

hardstatus alwayslastline
hardstatus string "%{= kw}%-w%{= BW}%n %t%{= kw}%+w %= %H %l %Y-%m-%d %c"
```

`defscrollback` — сколько строк доступно в `Ctrl-a [`. `hardstatus` — полоса снизу: окна, хост, load, время. Файл читается при **создании** сессии; уже живущие подхватят только после пересоздания.

Системный `/etc/screenrc` трогать не обязательно: пользовательский дополняет его.

## Частые ошибки

| Симптом | Почему | Что делать |
| --- | --- | --- |
| `There is no screen to be resumed` | имя другое или сессия уже умерла | `screen -ls`, затем точное имя |
| `Attached` и `-r` отказывается | сессия висит на другом pty | `screen -d -r имя` |
| Сессия пропала после команды | процесс в единственном окне завершился | обернуть в `bash -lc '…; exec bash'` |
| `Ctrl-a` «съедает» emacs/tmux внутри | префикс screen перехватывает | `Ctrl-a a` для литерала; либо другой escape в `.screenrc`: `escape ^Bb` |
| Нет вертикального сплита | Screen &lt; 4.1 | горизонтальный `Ctrl-a S` или поставить пакет новее |
| Лога нет | `-L` не был задан и `Ctrl-a H` не жали | включить явно, проверить cwd сессии |

Сокеты лежат в `/run/screen/S-$USER/` или `~/.screen/`. Чужую сессию так просто не взять: права на каталог.

## Когда screen, когда нет

Одна фоновая команда без интерактива — достаточно `systemd-run --user`, `tmux` или даже `nohup`. Постоянная рабочая среда на bastion, деплой «запустил и закрыл крышку», совместный просмотр логов — screen закрывает это без лишних зависимостей.

`tmux` удобнее сплитами и конфигом. Screen выигрывает тем, что **уже есть** на ящике, куда вы только что зашли по SSH. Именованная сессия и `Ctrl-a d` — тот минимум, из-за которого его держат в привычке.
