Learning Platform
Глоссарий Troubleshooting
Урок 11.05 · 22 мин
Начальный
jobsbackgroundforegroundnohupdisownCtrl-Zscreentmux

Jobs: foreground, background, nohup, disown

Когда вы в shell запускаете команду, по умолчанию она работает на переднем плане (foreground) — занимает терминал до завершения. Если команда длинная (бэкап на 2 часа, копирование 100GB через rsync), вы не можете в этом терминале делать ничего другого.

Для этого есть background jobs — способ запускать процессы «фоном», вернуться к ним позже, и (главное для DE) — оставлять их работать после того, как закроется ваше SSH-соединение с сервером.


& — запуск в фон

Самое простое — добавить & в конец команды:

# Запустить sleep на 300 секунд в фон
sleep 300 &
# [1] 12345

[1] — это job ID (внутри shell). 12345 — это PID. Job ID — это нумерация внутри shell для удобства управления, PID — глобальный.

После & команда уходит в фон, и shell возвращает вам приглашение. Вы можете запускать другие команды, пока та работает.

Foreground vs Background

Куда уходит процесс при разных способах запуска

sleep 100Запуск без &. Процесс работает на foreground, занимает терминал. Прерывание — Ctrl-C
терминал занят
один процесс одновременноПока не завершится — вы не можете в этом терминале делать ничего
sleep 100 &Запуск с &. Процесс уходит в background, shell возвращает prompt сразу
терминал свободен
много процессов параллельноМожете запустить ещё несколько. Управление через jobs/fg/bg

jobs — список текущих задач shell

sleep 100 &
sleep 200 &
sleep 300 &

jobs
# [1]   Running     sleep 100 &
# [2]-  Running     sleep 200 &
# [3]+  Running     sleep 300 &

Колонки:

  • [1], [2], [3] — Job ID (нумерация локальная для shell).
  • + — current job (default для fg/bg без аргументов).
  • - — previous job.
  • Running / Stopped / Done — состояние.
  • sleep 100 & — команда.
# Только PID-ы
jobs -p

# Только запущенные
jobs -r

# Только остановленные
jobs -s

fg, bg — перемещение между состояниями

fg (foreground) — перенести job на передний план. bg (background) — запустить остановленный job в фоне.

sleep 100 &
# [1] 12345
sleep 200 &
# [2] 12346

# Вернуть job 1 на передний план
fg %1
# (теперь sleep 100 на foreground, до завершения)

# Если просто fg без аргумента — current job (с +)
fg

# Аналогично bg
bg %2

%1, %2 — это синтаксис job spec:

Job spec — варианты обращения

Разные способы указать конкретную задачу

%1Job по номеру. Так делать чаще всего и удобнее
%+Current job. Аналогично просто %
%-Previous job
%sleepJob, команда которого начинается со 'sleep'
%?keywordJob, команда которого содержит 'keyword'
sleep 100 &
sleep 200 &
vim file.txt &

fg %vim      # перенести vim на foreground (по началу команды)
fg %?file    # по подстроке
fg %2        # по номеру

Ctrl-Z — приостановить и в фон

Сценарий: вы запустили vim file.txt, и поняли, что вам нужно выполнить ещё одну команду в shell — но не хотите закрывать vim. Решение:

  1. Нажимаете Ctrl-Z — vim приостанавливается (получает SIGTSTP).
  2. Shell возвращает prompt. Вы что-то делаете.
  3. Возвращаетесь в vim: fg.
vim file.txt
# В vim нажми Ctrl-Z
# [1]+  Stopped    vim file.txt

# Сейчас в shell. Можешь что-то сделать
ls
date

# Вернуться в vim
fg

Альтернативно — после Ctrl-Z запустить его в фон через bg. Это полезно если процесс может работать без терминала, например tail -f:

tail -f /var/log/airflow.log
# Ctrl-Z
# [1]+  Stopped    tail -f /var/log/airflow.log

bg
# [1]+ tail -f /var/log/airflow.log &
# Теперь tail работает в фоне (но всё ещё пишет в твой терминал — будет мешать)

Главная проблема background jobs: SIGHUP

Все background jobs привязаны к вашему shell. Когда shell завершается (вы закрыли SSH, разлогинились, упало соединение) — shell посылает SIGHUP всем своим jobs. По умолчанию SIGHUP -> процесс умирает.

Это критичная проблема для DE. Запустили бэкап на 2 часа через &, разлогинились — бэкап убит.

Решение — nohup или disown.


nohup — игнорировать SIGHUP

nohup (no hangup) запускает команду в режиме «игнорировать SIGHUP»:

nohup ./long_backup.sh &
# nohup: ignoring input and appending output to 'nohup.out'
# [1] 12345

Что делает nohup:

  1. Запускает команду, игнорируя SIGHUP.
  2. Перенаправляет stdout/stderr в файл nohup.out (если не указано иное).
  3. Отвязывает stdin (no input).

После этого можно безопасно закрыть терминал — процесс продолжит работать.

# С кастомным выходом
nohup ./backup.sh > /var/log/backup.log 2>&1 &

# Проверить, что работает после релогина
ssh server
pgrep -af backup.sh
tail -f /var/log/backup.log
TIP

nohup не делает «daemon-изацию» в строгом смысле. Процесс остаётся child вашего терминала-shell (точнее, init после переподписки). Это «good enough» для большинства задач, но для production-сервисов используют systemd (модуль 15).


disown — отвязать job от shell

disown — другой способ. Запустите процесс как обычно с &, потом отвяжите:

./backup.sh &
# [1] 12345

disown
# Теперь job 1 не в списке shell, не получит SIGHUP при закрытии

disown без аргументов отвязывает current job. Можно явно:

disown %1      # конкретный job
disown -a      # все jobs
disown -h %1   # не удалять из списка, но не слать SIGHUP

Преимущество disown — гибкость: можно сначала запустить, посмотреть что начало работать как надо, и потом «отвязать». А nohup нужно решать заранее.

Недостаток — stdout/stderr остаются привязанными к терминалу. После закрытия терминала может быть проблема — куда писать? Поэтому если используете disown, всё равно перенаправляйте output в файл:

./backup.sh > /var/log/backup.log 2>&1 &
disown

Сравнение: & vs nohup & vs disown vs screen/tmux

Способы фоновых задач — что выбрать

Когда какой инструмент уместен

cmd &Базовое. Job в текущем shell. Умрёт при закрытии shell. Для коротких задач во время одной сессии
nohup cmd &Игнорирует SIGHUP, output в nohup.out. Хорошо для «запустил-забыл», когда нужно пережить разлогин
cmd & disownЗапустил, посмотрел, отвязал. Гибче nohup, но output остаётся на терминал — нужен redirect
screen / tmuxТерминальный мультиплексор. Создаёт persistent session, в которую можно «возвращаться». Лучший вариант для долгих интерактивных задач
systemd unitДля production-сервисов. Автостарт, restart on crash, логи в journald. Модуль 14
systemd — правильный способ запускать долгосрочные процессы

screen и tmux мы подробно разбираем в модуле 19 (продуктивность). Главная идея: они создают «виртуальный терминал», который продолжает существовать даже после закрытия SSH. Вы возвращаетесь — и видите тот же экран, как будто никуда не уходили. Для долгих интерактивных задач — стандарт de facto в DE/DevOps.

# Превью — модуль 19 даст детально

# Создать tmux-сессию
tmux new -s backup

# Внутри tmux запустить долгую задачу
./backup.sh

# Отключиться от сессии (Ctrl-B, потом d)

# Закрыть SSH, переподключиться

# Вернуться в сессию
tmux attach -t backup

DE-сценарии

1. Долгий rsync на удалённый сервер

# Плохо — потеряется при разлогине
rsync -avz huge_data/ user@backup-server:/data/

# Хорошо
nohup rsync -avz huge_data/ user@backup-server:/data/ > rsync.log 2>&1 &
echo "Started PID $!"
# Можно закрыть SSH

Альтернатива (часто лучше): запустить в tmux/screen сессии, получить визуальный прогресс rsync, отключиться через Ctrl-B d, вернуться через tmux attach.

2. Параллельный запуск нескольких заданий

# Скачать 10 файлов параллельно
for url in $(cat urls.txt); do
  wget "$url" &
done

# Дождаться завершения всех
wait
echo "Все скачаны"

wait без аргументов — ждать всех background jobs. С аргументом wait %1 — конкретного.

3. Параллельный rsync для разных директорий

nohup rsync -avz /data/2026-04/ backup:/data/2026-04/ > 04.log 2>&1 &
nohup rsync -avz /data/2026-03/ backup:/data/2026-03/ > 03.log 2>&1 &
nohup rsync -avz /data/2026-02/ backup:/data/2026-02/ > 02.log 2>&1 &

# Все три работают независимо
jobs

4. Запустил-забыл из cron

# В crontab:
0 2 * * * /opt/scripts/nightly_etl.sh > /var/log/etl.log 2>&1

В cron — не нужен nohup/disown, потому что cron сам не привязан к терминалу. Достаточно перенаправления stdout/stderr. Модуль 15 про cron.

5. Hot-reload Airflow scheduler без downtime

# Старый scheduler ещё работает
OLD_PID=$(pgrep -x airflow-scheduler)

# Запускаем новый рядом
nohup airflow scheduler > /var/log/airflow/scheduler.log 2>&1 &
NEW_PID=$!

# Проверяем, что новый поднялся
sleep 5
pgrep -af airflow-scheduler

# Гасим старый
kill -TERM $OLD_PID

В реальности — systemctl restart, но понимать механику полезно.


Подводные камни

1. Output в фон захламляет терминал

Если background job пишет в stdout, текст вылезает поверх вашего prompt. Бесит. Решение — перенаправление:

# Плохо
nohup ./script.sh &

# Хорошо
nohup ./script.sh > /tmp/script.log 2>&1 &

2>&1 — модуль 9, перенаправление stderr в stdout.

2. Bash shell вы видите как-будто завис

Когда вы запускаете cmd &, bash печатает [1] 12345 и возвращает prompt. Но когда job завершается, bash хочет сообщить «[1] Done sleep 100» — он печатает это перед следующим prompt. Если у вас открыт интерактивный prompt, выглядит как будто что-то само напечаталось.

3. Process group и Ctrl-C

Если вы запустили несколько процессов в pipeline cmd1 | cmd2 | cmd3 & — это одна job, состоящая из трёх процессов в одной process group. Ctrl-C / SIGINT идёт всей группе.

sleep 100 | sleep 200 | sleep 300 &
jobs
# [1]+  Running    sleep 100 | sleep 200 | sleep 300 &
ps -j

Попробуй сам

# 1. Базовое
sleep 100 &
sleep 200 &
jobs
fg %1   # вернул sleep 100 на foreground
# Прерви Ctrl-C
jobs    # теперь только sleep 200

# 2. Ctrl-Z + bg
sleep 100
# Нажми Ctrl-Z
# [1]+  Stopped    sleep 100
bg
jobs
# [1]+  Running    sleep 100 &

# 3. nohup пережил SSH
nohup sleep 600 > /tmp/sleep.log 2>&1 &
SLEEP_PID=$!
# Откройте новый ssh / закройте текущий, переподключитесь
ps -p $SLEEP_PID   # всё ещё работает!
kill $SLEEP_PID

# 4. disown
./long-task.sh &
disown
# Закрой SSH — процесс выживет

# 5. wait для синхронизации
echo "Старт"
(sleep 3; echo "Готово 1") &
(sleep 2; echo "Готово 2") &
(sleep 4; echo "Готово 3") &
wait
echo "Все готовы"

# 6. Параллельный download (если есть wget)
for n in 1 2 3 4 5; do
  (echo "download $n"; sleep 2; echo "done $n") &
done
wait

Cross-link: предыдущий урок 04 — про SIGHUP, который убивает background jobs. Модуль 17 — production bash, где обсуждаем trap и graceful shutdown. Модуль 18 — screen/tmux для долгих задач. Модуль 14 — systemd для production-сервисов.


Проверка знанийKnowledge check
Вы по SSH запустили на сервере «./backup.sh &». Скрипт работает 4 часа. Вы планируете закрыть ноутбук и пойти спать. Что произойдёт со скриптом и какой паттерн правильный?
ОтветAnswer
Когда вы закроете SSH (или просто потеряете соединение), shell на сервере получит SIGHUP. По умолчанию shell посылает SIGHUP всем своим background jobs, и они умрут. Ваш «./backup.sh &» будет убит на середине, оставив частичный бэкап. Правильные варианты: 1) «nohup ./backup.sh > /var/log/backup.log 2>&1 &» — nohup игнорирует SIGHUP и перенаправляет output в файл. 2) Запустить в tmux/screen сессии (модуль 19) — лучший вариант, можно вернуться и посмотреть прогресс. 3) Запустить ./backup.sh &, потом «disown» — отвязывает job от shell, но output всё равно нужно перенаправить в файл, иначе после закрытия SSH stdout некуда писать. 4) В production — systemd unit (модуль 15), но для разового бэкапа это перебор. Самый идиоматичный выбор для разовой задачи на 4 часа — tmux. Для регулярных — cron или systemd timer.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Какая комбинация клавиш приостанавливает процесс на foreground и переводит его в background-stopped?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5