Как я могу запустить команду, которая будет работать после закрытия терминала?

Вопрос или проблема

Иногда я хочу запустить процесс и забыть о нем. Если я запускаю его из командной строки, вот так:

redshift

Я не могу закрыть терминал, иначе это убьет процесс. Могу ли я запустить команду так, чтобы мог закрыть терминал, не убив процесс?

Одна из следующих двух команд должна сработать:

$ nohup redshift &

или

$ redshift &
$ disown

Смотрите следующее для немного большей информации о том, как это работает:

Если ваша программа уже запущена, вы можете приостановить ее с помощью Ctrl-Z, перевести в фоновый режим с помощью bg, а затем disown, вот так:

$ sleep 1000
^Z
[1]+  Stopped                 sleep 1000
$ bg
$ disown
$ exit

Хороший ответ уже опубликован @Steven D, но я думаю, что это может немного прояснить ситуацию.

Причина, по которой процесс убивается при завершении терминала, состоит в том, что процесс, который вы запускаете, является дочерним процессом терминала. Как только вы закрываете терминал, это убивает и эти дочерние процессы. Вы можете увидеть дерево процессов с помощью pstree, например, когда запускаете kate & в Konsole:

init-+
     ├─konsole─┬─bash─┬─kate───2*[{kate}]
     │         │      └─pstree
     │         └─2*[{konsole}]

Чтобы сделать процесс kate независимым от konsole при завершении konsole, используйте nohup с командой, вот так:

nohup kate &

После закрытия konsole, pstree будет выглядеть так:

init-+
     |-kate---2*[{kate}]

и kate выживет. 🙂

Альтернативой является использование screen/tmux/byobu, которые будут держать оболочку работающей, независимо от терминала.

Вы можете запустить процесс так в терминале

setsid process

Это запустит программу в новой сессии, как объясняется в моей статье здесь.

Я предпочитаю:

(applicationName &)

например:
linux@linux-desktop:~$ (chromium-browser &)

Не забудьте использовать скобки при вводе команды!

Хотя все предложенные варианты хорошо работают, я нашел, что мой альтернативный вариант — использовать screen, программу, которая устанавливает виртуальный терминал на вашем экране.

Вы можете рассмотреть возможность запуска с screen -S session_name. Screen можно установить практически на всех дистрибутивах Linux и Unix. Нажимая Ctrl+A и (строчная) C, вы начнете вторую сессию. Это позволит вам переключаться между первоначальной сессией, нажимая Ctrl+A и 0, или новой сессией, нажимая Ctrl+A и 1. Вы можете иметь до десяти сессий в одном терминале. Раньше я запускал сессию на работе, возвращался домой, подключался по ssh к своему рабочему компьютеру, а затем вызывал screen -d -R session_name. Это позволит вам повторно подключиться к этой удаленной сессии.

У меня есть скрипт (я назвал его run), чтобы:

  • Запускать произвольные команды в фоне
  • Останавливать их от завершения с закрытием окна терминала
  • Подавлять их вывод
  • Обрабатывать код завершения

Я использую его в основном для gedit, evince, inkscape и т. д., у которых есть много раздражающего вывода в терминал. Если команда завершается до TIMEOUT, возвращается код завершения nohup, а не ноль. Содержимое run:

#!/bin/bash

TIMEOUT=0.1

#используйте nohup для запуска команды, подавляя ее вывод и позволяя закрывать терминал
#также отправьте вывод nohup в /dev/null, подавляя nohup.out
#запускайте nohup в фоне, чтобы этот скрипт не блокировал
nohup "${@}" >/dev/null 2>&1 &
NOHUP_PID=$!

#убийте этот скрипт через короткое время, выходя с успешным статусом - команда все еще работает
#это необходимо, так как у `wait` ниже нет аргумента таймы
MY_PID=$$
trap "exit 0" SIGINT SIGTERM
sleep $TIMEOUT && kill $MY_PID 2>/dev/null & #игнорировать ошибку "Нет такого процесса", если это завершится нормально

#если команда завершается до вышеуказанного времени ожидания, все может быть в порядке или могла произойти ошибка
wait $NOHUP_PID
NOHUP_STATUS=$?
#напечатайте ошибку, если таковая имелась. Чаще всего в команде была опечатка
[ $NOHUP_STATUS != 0 ] && echo "Ошибка ${@}"
#возвращаем код завершения nohup, каким бы он ни был
exit $NOHUP_STATUS

Примеры:

>>> run true && echo success || echo fail
success
>>> run false && echo success || echo fail
Ошибка false
fail
>>> run sleep 1000 && echo success || echo fail
success
>>> run notfound && echo success || echo fail
Ошибка notfound
fail

Способ сделать все это только с помощью оболочки — закрыть stdin и отправить команду в фон:

command <&- & 

Т тогда она не завершится, когда вы выйдете из оболочки. Перенаправление stdout — приятное дополнительное действие.

Недостаток в том, что вы не можете сделать это задним числом.

Вы можете установить процесс (PID) так, чтобы он не получал сигнал HUP при выходе и завершении сеанса терминала. Используйте следующую команду:

nohup -p PID

Можно утверждать, что это похоже на ответ, предложенный apolinsky, я использую вариант на screen. Обычная команда выглядит так

screen bash -c 'long_running_command_here; echo; read -p "ALL DONE:"'

Сессию можно отключить с помощью Ctrl ACtrl D и повторно подключиться к простой сессии с помощью screen -r. У меня это завёрнуто в скрипт под названием session, который находится в моем PATH для удобного доступа:

#!/bin/bash
#
if screen -ls | awk '$1 ~ /^[1-9][0-9]*\.'"$1""https://unix.stackexchange.com/" >/dev/null
then
    echo "WARNING: сессия уже запущена (переподключитесь с помощью 'screen -r $1')" >&2
else
    exec screen -S "$1" bash -c "$@; echo; echo '--------------------'; read -p 'ALL DONE (Enter to exit):'"
    echo "ERROR: 'screen' не установлен на этой системе" >&2
fi
exit 1

Это работает только тогда, когда вы заранее знаете, что хотите отключить программу. Это не позволяет отключить уже запущенную программу.

Аналогично другим ответам, опубликованным ранее, можно перенести запущенный процесс в screen с помощью reptyr и затем закрыть терминал. Шаги описаны в этом посте.
Необходимые шаги:

  1. Приостановите процесс
  2. Возобновите процесс в фоне с помощью bg
  3. disown процесс
  4. Запустите сессию screen
  5. Найдите PID процесса
  6. Используйте reptyr для перехвата процесса

Ответ или решение

Запуск процесса в Linux, который продолжит работать после закрытия терминала, представляет собой распространённую задачу для пользователей и разработчиков. Существует несколько методов, позволяющих сделать это, и в этом ответе мы подробно рассмотрим основные из них, а также последствия каждого подхода.

1. Использование nohup

Команда nohup, сокращение от "no hang up", позволяет запустить процесс, который не будет прекращаться после выхода из терминала. Синтаксис следующий:

nohup redshift &

Здесь & помещает процесс в фоновый режим, позволяя вам продолжать работу в терминале или закрыть его. nohup автоматически перенаправляет стандартный вывод и стандартный поток ошибок в файл nohup.out, если они не перенаправлены явно.

2. Использование disown

Если вы уже запустили процесс и хотите его отсоединить, вы можете использовать команды bg и disown. Например:

sleep 1000
^Z
bg
disown
  • ^Z приостанавливает процесс (т.е. отправляет его в фон);
  • bg вновь запускает его в фоновом режиме;
  • disown удаляет его из списка задач текущего сеанса, позволяя закрыть терминал.

3. screen и tmux

Эти утилиты позволяют создавать виртуальные терминалы, которые продолжают работать даже после закрытия соединения. Для начала работы с screen, выполните:

screen -S имя_сессии

Внутри сессии вы можете запускать любые команды. Чтобы отделить сессию, нажмите Ctrl+A, затем D. Позже вы сможете подключиться к сессии с помощью:

screen -r имя_сессии

В tmux аналогичные действия, предоставляющие более широкие возможности сессий, можно выполнить так:

tmux new -s имя_сессии

4. setsid

Команда setsid запускает новую сессию, что позволяет процессу продолжать работу независимо от родительского терминала:

setsid redshift

5. Фоновый запуск с закрытием stdin

Запуск процесса с перенаправлением ввода:

command <&- &

Этот метод закрывает стандартный поток ввода, что предотвращает завершение процесса при закрытии терминала.

6. Использование скрипта run для управления процессами

Можно создать скрипт, который будет управлять фоновыми задачами с использованием nohup и скрытого вывода. Пример скрипта:

#!/bin/bash

nohup "$@" >/dev/null 2>&1 &

Этот скрипт принимает команду как аргумент и выполняет ее в фоновом режиме, скрывая вывод.

7. Использование reptyr для переноса процессов

Если процесс уже запущен, но вы хотите отделить его от терминала, можно использовать reptyr. Этот инструмент позволяет перехватить запущенный процесс и присоединить его к сессии screen или tmux. Инструкции:

  1. Приостановите текущий процесс (Ctrl+Z).
  2. Запустите его в фоновом режиме (bg).
  3. Используйте disown.

После этого войдите в новую сессию screen и используйте reptyr PID, где PID — идентификатор процесса, который вы хотите перенести.

Заключительные рекомендации

Выбор метода зависит от ваших потребностей и сценария использования. nohup подходит для простых задач, в то время как screen и tmux предлагают более гибкие возможности управления сессиями. Будьте внимательны при использовании этих команд, чтобы избежать неожиданных остановок процессов.

Правильное применение этих техник поможет вам эффективно управлять задачами в Linux и обеспечит бесперебойную работу важных процессов.

Оцените материал
Добавить комментарий

Капча загружается...