Убить сценарий оболочки, работающий в фоновом режиме

12

Я написал сценарий оболочки для мониторинга каталога, используя утилиту inotifywait inotifyt-tools. Я хочу, чтобы этот сценарий работал непрерывно в фоновом режиме, но я также хочу иметь возможность остановить его при желании.

Чтобы он работал непрерывно, я использовал while true; как это:

while true;
do #a set of commands that use the inotifywait utility
end

Я сохранил его в файле /binи сделал его исполняемым. Чтобы он работал в фоновом режиме, я использовал nohup <script-name> &и закрыл терминал.

Я не знаю, как мне остановить этот сценарий. Я посмотрел на ответы здесь и очень тесно связанный вопрос здесь .

ОБНОВЛЕНИЕ 1: На основе ответа @InfectedRoot ниже, я смог решить мою проблему, используя следующую стратегию. Первое использование

ps -aux | grep script_name

и использовать, sudo kill -9 <pid>чтобы убить процессы. Затем мне пришлось pgrep inotifywaitи sudo kill -9 <pid>снова использовать для возвращенного идентификатора.

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

ОБНОВЛЕНИЕ 2: Ответ состоит из убийства 2 процессов . Это важно, потому что запуск сценария в командной строке инициирует 2 процесса, 1 сам сценарий и 2 процесс inotify .

light94
источник
2
Удаление -9опции из killи использование только killуберет беспорядок.
Сри
1
Браво. Чтобы снова получить «$$» в кассу, я хотел бы подчеркнуть, что этот -9вариант будет полным kill. : P
синтаксическая ошибка
Для полноты: существует чистый подход для уничтожения обоих процессов одновременно: вместо того, чтобы убивать процессы по отдельности, вы можете уничтожить группу процессов сценария, чтобы убить их сразу. PGID такая же , как PID основного процесса сценария оболочки и killэто вы префикс PGID с минусом: kill -- -<pgid>, kill -9 -<pgid>и т.д.
cg909

Ответы:

6

Чтобы улучшить, использовать killall, а также комбинировать команды:

ps -aux | grep script_name
killall script_name inotifywait

Или сделать все в одной строке:

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait
сметана
источник
Ваше решение выше, но не однострочная команда. Пожалуйста, не могли бы вы проверить это. Я получаю сообщение об ошибке: нет процесса
light94
@ light94 Error: no processдолжно означать, что вы его уже убили. Вы можете проверить это, открыв две другие программы, такие как VLC & Geany , и попробовать их вместо них.
Cremefraiche
Привет @cremefraiche, как сказал раньше, твой код работает ... но я искал альтернативные решения, и самое распространенное решение, которое я вижу, это использовать kill <pid>. Так как мой сценарий не заканчивается таким образом, я немного волнуюсь, если мой код неправильный? Можете ли вы направить меня?
light94 14.12.14
Вы можете сэкономить на grep -v grep, превратив grep script_nameв grep -e "[s]cript_name".
nmichaels
3

Перечислите фоновые задания, используя

# jobs

Затем выберите предыдущий номер задания и запустите

пример

# fg 1 

введите описание изображения здесь

выведет на передний план.

Затем убейте его, используя CTRL + C или более простой способ найти PID скрипта, используя

ps -aux | grep script_name

введите описание изображения здесь

Тогда убей, используя pid

sudo kill -9 pid_number_here
Бабин Лонстон
источник
2
Первый вариант недопустим для моего случая, потому что я закрываю оболочку после установки скрипта и думаю, что команда jobs работает только для той же оболочки. Кроме того, я попробовал второй вариант, и он действительно убивает процесс, но когда я делаю pgrep inotifywait, я все еще могу видеть его как процесс там.
light94
jobs даст список заданий, даже если вы закроете оболочку. Тем не менее процесс завершится, он будет там.
Бабин Лонстон
2
Я попробовал это, но это не так.
light94
@ light94 Вы правы. Здесь часто случалось, что jobsкоманда действительно отображала только запущенные задания в оболочке. Как только я закрыл определенное окно терминала, единственный способ получить к ним доступ был через ps(см. Выше). Так что наверняка ты не придумываешь это.
синтаксическая ошибка
2

Вы можете использовать ps+ grepили, pgrepчтобы получить имя процесса / pid ; позже используйте killall/, pkillчтобы убить имя процесса или используйте, killчтобы убить pid. Все следующие должны работать.

killall $(ps aux | grep script_name | grep -v grep | awk '{ print $1 }') && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $1 }' | xargs killall) && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $2 }' | xargs kill) && killall inotifywait
(pgrep -x script_name | xargs kill) && pkill -x inotifywait
pkill -x script_name && pkill -x inotifywait

Самое главное, что вы должны убедиться, что вы убиваете только те процессы, которые вы надеетесь убить.

pkill/ pgrepсоответствует шаблону, а не точному имени , поэтому более опасен; здесь -xдобавляется, чтобы соответствовать точному названию.

Также при использовании pgrep/ pkillвам может понадобиться

  • -fсоответствовать полной командной строке (как ps auxделает)
  • -a также напечатать имя процесса.
Хунсю Чен
источник
После выполнения вышеизложенного я получаю ошибку использования в каждом. Я копирую вставку и заменяю имя скрипта. Я получаю использование kill для вывода
light94
Script_name работает? Это работает для меня (Debian Джесси)
Хунсю Чен
да, это работает. и ваше решение почти такое же, как и у решений @cremefraiche, описанных выше, поэтому я ожидал, что оно будет работать так же: /
light94
Да, я несколько суммировал возможные способы сделать это, особенно добавив pgrep/ pkill. Если это все еще не так, вы можете попробовать pgrepбез -x. По сути, я не думаю, что хорошо убивать процесс в одной строковой команде, не проверяя, совпадают ли другие процессы.
Хунсю Чен,
1

Как вы, вероятно, можете сказать, есть много способов сделать это.

Что касается вашего «ОБНОВЛЕНИЯ № 2» - вообще говоря, завершение любого процесса в иерархии родитель-потомок, как правило, завершит все связанные процессы. Но есть много исключений из этого. В идеале вы хотите завершить последний «потомок» в дереве процессов, тогда родитель (ы) этого потомка должны выйти, если у них нет других задач для выполнения. Но если вы убиваете родителя, сигнал должен передаваться потомкам, когда родитель умирает, и потомки также должны выходить - но есть случаи, когда дочерние процессы могут игнорировать сигнал (через ловушки или подобные механизмы) и могут продолжаться выполнение будет наследоваться процессом 'init' (или подобным). Но этот предмет поведения процесса может стать сложным, и я просто оставлю его там ...

Один метод, который мне нравится, если я не хочу использовать скрипт управления (описан далее), - это использовать утилиту 'screen' для запуска и управления процессом. Команда «screen» полна функций и может занять некоторое время для освоения. Я бы посоветовал вам прочитать справочную страницу «screen» для полного объяснения. Быстрый пример запуска процесса в фоновом режиме - команда:

screen -d -m / path / to / program

Это запустит "/ path / to / program" внутри сеанса 'screen'.

Вы можете увидеть ваш сеанс бега с помощью команды:

скрин-ллы

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

экран -r

А затем просто завершите его с помощью ^ C или чего-то еще.

Помимо красоты возможности повторного подключения и отключения от вашего процесса по желанию является то, что «screen» будет захватывать любой stdout (), который может создать ваша программа.


Но мое личное предпочтение в этих вопросах - иметь контрольную программу, которая управляет запуском и остановкой процесса. Это может быть несколько сложным и требует некоторых возможно сложных сценариев. И как любой сценарий, есть множество хороших способов сделать это. Я включил пример bash метода, который обычно использую для запуска и остановки приложений. Если ваша задача проста, вы можете вставить ее непосредственно в скрипт управления - или вы можете заставить этот скрипт управления вызывать другую внешнюю программу. Обратите внимание, что этот пример ни в коем случае не является исчерпывающим с точки зрения управления процессом. Я исключил возможность таких сценариев, как: проверка того, что сценарий еще не запущен, когда вы используете опцию «запуск», проверка того, что запущенный PID на самом деле является процессом, который вы запустили (например, ваш сценарий не ' t и другой процесс был запущен с использованием того же PID), и проверка того, что скрипт действительно ответил (вышел) на первый запрос kill. Выполнение всех этих проверок может быть сложным, и я не хотел делать пример слишком длинным и сложным. Возможно, вы захотите изменить пример, чтобы попрактиковаться в написании сценариев оболочки.

Сохраните следующий код в файл с именем «programctl», сделайте его исполняемым с помощью команды:

CHMOD 755 программ

Затем отредактируйте файл и добавьте ваш код / ​​скрипт в case-раздел, который начинается с «myscript».

Когда все на месте, при условии, что «programctl» находится в текущем каталоге, вы можете запустить вашу программу с помощью:

./programctl начало

И остановите это с:

./programctl stop

Приветствия.

#!/bin/bash
# Description:  A wrapper script used to stop/start another script.

#--------------------------------------
# Define Global Environment Settings:
#--------------------------------------

# Name and location of a persistent PID file

PIDFILE="/tmp/tmpfile-$LOGNAME.txt"

#--------------------------------------
# Check command line option and run...
# Note that "myscript" should not
# provided by the user.
#--------------------------------------

case $1
in
    myscript)
        # This is where your script would go.
        # If this is a routine 'bash' shell script, you can enter
        # the script below as illustrated in the example.  
        # Or you could simply provide the path and parameters
        # to another script such as /dir/name/command -options

        # Example of an embedded script:

        while true
        do
            # do something over and over...
            sleep 1
        done

        # Example of an external script:

        /usr/local/bin/longrun -x
    ;;

    start)
        # Start your script in the background.
        # (Note that this is a recursive call to the wrapper
        #  itself that effectively runs your script located above.)
        $0 myscript &

        # Save the backgound job process number into a file.
        jobs -p > $PIDFILE

        # Disconnect the job from this shell.
        # (Note that 'disown' command is only in the 'bash' shell.)
        disown %1

        # Print a message indicating the script has been started
        echo "Script has been started..."
    ;;

    stop)
        # Read the process number into the variable called PID
        read PID < $PIDFILE

        # Remove the PIDFILE
        rm -f $PIDFILE

        # Send a 'terminate' signal to process
        kill $PID

        # Print a message indicating the script has been stopped
        echo "Script has been stopped..."
    ;;

    *)
        # Print a "usage" message in case no arguments are supplied
        echo "Usage: $0 start | stop"
    ;;
esac
Кристофер Моррис
источник
Я попробовал предложенный вами метод «экрана», и он прекрасно работает. Мне еще предстоит попробовать второй способ. Спасибо за ответ.
light94