Вбивство сценарію оболонки працює у фоновому режимі


12

Я написав сценарій оболонки для моніторингу каталогу за допомогою утиліти inotifywait інструментів inotifyt. Я хочу, щоб цей сценарій постійно працював у фоновому режимі, але я також хочу, щоб він міг зупинити його за бажанням.

Щоб він працював постійно, я використовував 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>знову використовувати для повернутого id.

Це працює, але я думаю, що це безладний підхід, я шукаю кращої відповіді.

ОНОВЛЕННЯ 2: Відповідь полягає у вбивстві 2 процесів . Це важливо, тому що запуск сценарію в командному рядку ініціює 2 процеси, 1 сам сценарій та 2, ініціативний процес .



2
Якщо вилучити -9параметр з killі просто killвикористати, це забруднить його.
Sree

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

Ви можете заощадити на 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

вакансії дадуть список завдань, навіть якщо ви закриєте оболонку. І все-таки процес закінчиться, він буде там.
Бабін Лонстон

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 також надрукувати ім'я процесу.

Після виконання вищезазначеного я отримую помилку використання в кожному. я копіюю вставлення та заміну імені сценарію. Я використовую для вбивства як вихід
light94

Чи виконується ім'я_користувача? Це працює для мене (debian jessie)
Hongxu Chen,

так, це працює. і ваше рішення майже таке ж, як і рішення @cremefraiche вище, тому я очікував, що він буде працювати так само: /
light94

Так, я дещо підсумовував можливі способи зробити це, особливо додаючи pgrep/ pkill. Якщо це все-таки помиляється, ви можете спробувати pgrepбез -x. В основному, я не думаю, що це добре, щоб вбити процес в межах однієї рядкової команди, не перевіряючи, чи відповідають інші процеси.
Hongxu Chen

1

Як ви, напевно, можете сказати, існує маса способів зробити це.

Щодо вашого "ОНОВЛЕННЯ №2" - загалом кажучи, припинення будь-якого процесу в ієрархії батько-дитина, як правило, припиняє всі пов'язані з цим процеси. Але є багато винятків із цього. В ідеалі, ви хочете припинити остаточне "дочірнє" у дереві процесів, тоді батьки цієї дитини повинні вийти, якщо у них немає інших завдань для запуску. Але якщо ви вбите батьків, сигнал повинен бути переданий дітям, коли батько помирає, і діти також повинні вийти - але є випадки, коли діти-процеси можуть ігнорувати сигнал (через пастки або подібні механізми) і можуть тривати для запуску заповіту буде успадкований процес 'init' (або подібний.) Але цей предмет поведінки процесу може стати складним, і я просто залишу його там ...

Один із способів, який мені подобається, якщо я не хочу використовувати контрольний скрипт (описаний далі), - це використовувати утиліту 'screen' для запуску та управління процесом. Команда 'screen' має багато функцій і може зайняти деякий час, щоб освоїти Я б закликав вас прочитати головну сторінку "екрана" для повного пояснення. Швидкий приклад запустити процес у фоновому режимі - це команда:

екран -d -m / шлях / до / програма

Це розпочнеться "/ шлях / до / програми" всередині сеансу "екран".

Ви можете побачити свій запущений сеанс за допомогою команди:

екран -л

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

екран -r

А потім просто припиніть його з ^ С або чим завгодно.

Окрім краси того, що можна за власним бажанням знову підключитися та відключитися від процесу, це те, що на екрані буде відображено будь-який stdout (), який може створити ваша програма.


Але мої особисті переваги в цих питаннях - мати контрольну програму, яка керує запуском і зупинкою процесу. Це може стати дещо складним і вимагає певних складних сценаріїв. І як і будь-який сценарій, є десятки хороших способів зробити це. Я включив базовий приклад методу, який я звичайно використовую для запуску та зупинки програм. Якщо ваше завдання просте, ви можете вставити його безпосередньо в керуючий скрипт - або ви можете закликати цей скрипт управління викликати іншу зовнішню програму. Зауважте, що цей приклад аж ніяк не є комплексним з точки зору управління процесом. Я покинув можливість таких сценаріїв, як: Переконайтесь, що сценарій вже не працює, коли ви використовуєте опцію "start", перевіряючи, що запущений PID - це фактично процес, який ви розпочали (наприклад, ваш скрипт hasn ' t помер, і інший процес був запущений з використанням того ж PID), і підтверджуючи, що сценарій насправді відповів (вийшов) на перший запит 'kill'. Виконання всіх цих перевірок може ускладнитися, і я не хотів робити приклад занадто довгим і складним. Можливо, ви захочете змінити приклад, щоб практикувати сценарій оболонки.

Збережіть наступний код у файлі під назвою "programctl", зробіть його виконуваним за допомогою команди:

chmod 755 programctl

Потім відредагуйте файл та додайте свій код / ​​скрипт у розділ справи, який починається з «myscript».

Як тільки все буде на місці, якщо припустити, що "programctl" знаходиться в поточному каталозі, ви можете запустити свою програму з:

./programctl start

І зупиніть це:

./programctl зупинка

Ура.

#!/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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.