← Весь справочник

Фоновые процессы: &, nohup, setsid, disown, jobs, wait

Запуск в фоне, управление заданиями bash и как сделать, чтобы процесс пережил закрытие терминала

Команда с & в конце запускается в фоне: оболочка не ждёт её завершения и сразу возвращает приглашение. Такие процессы — задания (jobs) текущей оболочки: их видно в jobs, можно вернуть на передний план и дождаться через wait. Но при закрытии терминала оболочка рассылает своим заданиям сигнал SIGHUP, и они завершаются. Чтобы процесс пережил выход из SSH, его отвязывают от терминала: nohup, disown или setsid.

Синтаксис

команда &                    # запустить в фоне
jobs [-l|-p|-r|-s]           # список заданий
fg [%N] / bg [%N]            # на передний план / продолжить в фоне
wait [-n] [PID|%N…]          # дождаться завершения
disown [-h] [-a] [%N]        # отвязать задание от оболочки
nohup команда [аргументы…] [&]
setsid [-f] [-w] команда [аргументы…]

Управление заданиями (встроено в bash)

Команда / клавишиЧто делает
команда &запустить в фоне; печатает [1] 4242 — номер задания и PID
$!PID последнего фонового процесса — сохрани в переменную
Ctrl+Zприостановить текущую команду (сигнал SIGTSTP), она станет Stopped
bg [%N]продолжить остановленное задание в фоне
fg [%N]вернуть задание на передний план
jobsсписок: [1]- Running sleep 100 &; + — текущее, - — предыдущее
jobs -l / -pс PID / только PID
jobs -r / -sтолько работающие / только остановленные
kill %Nпослать сигнал заданию по номеру

Ссылки на задания: %1 — по номеру, %+ или %% — текущее, %- — предыдущее, %sleep — по началу команды, %?str — по подстроке.

wait

ФормаЧто делает
waitждать все фоновые процессы; код возврата 0
wait PID / wait %Nждать конкретный; код возврата — код этого процесса
wait -nждать первый завершившийся из любых; его код возврата
wait -n -p varто же и записать PID завершившегося в var (bash 5.1+)

Если такого процесса нет среди детей оболочки — код 127.

Как пережить закрытие терминала

СпособКак работаетВывод
nohup cmd &запускает команду с игнорированием SIGHUPесли stdout — терминал, пишет в ./nohup.out (или ~/nohup.out), права 600
cmd & disownубирает задание из таблицы оболочки — она не пошлёт ему SIGHUPостаётся привязан к терминалу — перенаправь сам
disown -h %Nзадание остаётся в jobs, но SIGHUP ему не пошлютто же
setsid cmdзапускает в новой сессии без управляющего терминала; родителем становится PID 1то же — перенаправь сам
tmux / screenтерминал живёт на сервере, можно переподключитьсявсё видно после повторного входа
systemd-юнитправильный способ для долгоживущих сервисовjournalctl

Опции setsid: -f — всегда создавать новый процесс (вернуть управление сразу), -w — дождаться программы и вернуть её код, -c — сделать текущий терминал управляющим.

Примеры

# запустить в фоне и запомнить PID
sleep 300 &
pid=$!
jobs -l
kill "$pid"

# остановленную команду продолжить в фоне
# (нажал Ctrl+Z во время долгого cp)
bg
jobs

# параллельно несколько задач и дождаться всех, проверив коды
for host in web1 web2 web3; do
    ping -c1 -W1 "$host" >/dev/null 2>&1 &
done
wait

# дождаться конкретного и получить его код возврата
(sleep 1; exit 3) &
wait $!
echo "код: $?"        # 3

# переживёт выход из SSH; вывод — в свой лог, stdin закрыт
nohup ./long-task.sh > task.log 2>&1 < /dev/null &

# уже запустил без nohup — отвязать задним числом
./long-task.sh > task.log 2>&1 &
disown

# полностью отделить от терминала и сессии
setsid -f ./daemon.sh > /tmp/daemon.log 2>&1 < /dev/null

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

  • Обычное cmd & умирает при закрытии SSH. Интерактивная оболочка при получении SIGHUP рассылает его всем своим заданиям. nohup, disown, disown -h и setsid от этого защищают.
  • nohup не перенаправляет вывод, если он уже не терминал. nohup.out появляется только когда stdout — терминал. А disown и setsid не перенаправляют никогда: процесс, пишущий в закрытый терминал, получит ошибку записи и может упасть. Всегда указывай > файл 2>&1 < /dev/null.
  • nohup не делает & за тебя. nohup cmd без & займёт терминал до конца работы.
  • wait без аргументов возвращает 0, даже если задания упали. Нужен код каждого — сохраняй $! и вызывай wait "$pid" по очереди.
  • wait видит только своих детей. Процесс, запущенный из другой оболочки или ушедший в setsid -f, дождаться так нельзя: «is not a child of this shell», код 127. Для чужих процессов — цикл с kill -0 PID.
  • jobs и %N работают только в той оболочке, где запущено задание. В новом SSH-сеансе jobs пуст — ищи процесс через pgrep/ps.
  • Фоновый процесс, читающий с терминала, останавливается (Stopped (tty input)). Закрывай stdin: < /dev/null.
  • $! — PID последнего элемента конвейера a | b &, а не первого.
  • Для настоящих сервисов nohup — костыль: нет перезапуска при падении и нормальных логов. Используй systemd или supervisor.

См. также

kill · ps · timeout · tmux · redirection · supervisorctl · exit-codes