Uccidere uno script di shell in esecuzione in background


12

Ho scritto uno script di shell per monitorare una directory usando l'utilità inotifywait di inotifyt-tools. Voglio che lo script venga eseguito continuamente in background, ma voglio anche essere in grado di interromperlo quando lo si desidera.

Per farlo funzionare continuamente, ho usato while true; come questo:

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

L'ho salvato in un file /bine l'ho reso eseguibile. Per farlo funzionare in background, ho usato nohup <script-name> &e chiuso il terminale.

Non so come fermare questo script. Ho esaminato le risposte qui e una domanda molto strettamente correlata qui .

AGGIORNAMENTO 1: Sulla base della risposta di @InfectedRoot di seguito, sono stato in grado di risolvere il mio problema utilizzando la seguente strategia. Primo utilizzo

ps -aux | grep script_name

e usare sudo kill -9 <pid>per uccidere i processi. Ho quindi dovuto pgrep inotifywaite sudo kill -9 <pid>riutilizzarlo per l'ID restituito.

Funziona ma penso che questo sia un approccio disordinato, sto cercando una risposta migliore.

AGGIORNAMENTO 2: la risposta consiste nell'uccidere 2 processi . Questo è importante perché l'esecuzione dello script dalla riga di comando avvia 2 processi, 1 lo script stesso e 2, il processo di inotify .



2
La rimozione -9dell'opzione da kille l'utilizzo di Just killeliminerà il caos da essa.
Sree,

1
Bravo. Per inserire nuovamente un po 'di "$$" nella cassa del gioco di parole, vorrei sottolineare che l' -9opzione sarebbe totalmente killqui. : P
syntaxerror,

Per completezza: esiste un approccio pulito per uccidere entrambi i processi contemporaneamente: invece di uccidere i processi separatamente è possibile uccidere il gruppo di processi dello script per ucciderli contemporaneamente. Il pgid è lo stesso che il PID del processo principale script di shell e killche precedere la pgid con un meno: kill -- -<pgid>, kill -9 -<pgid>, ecc
cg909

Risposte:


6

Per migliorare, utilizzare killalle anche combinare i comandi:

ps -aux | grep script_name
killall script_name inotifywait

O fai tutto in una riga:

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait

La soluzione sopra indicata funziona ma non il comando a una riga. Per favore, puoi controllarlo. Ottengo un errore: nessun processo
light94,

@ light94 Error: no processdovrebbe significare solo che l'hai già ucciso. Puoi testarlo aprendo altri due programmi, come VLC e Geany , e provandolo con loro.
Cremefraiche,

Ehi, @cremefraiche, come diceva il vecchio, il tuo codice funziona .. ma ho cercato soluzioni alternative e la soluzione più comune che vedo è usare kill <pid>. Dal momento che il mio script non finisce in questo modo, sono un po 'preoccupato se il mio codice non è corretto? Potete per favore guidarmi?
light94

Puoi risparmiare sul grep -v greptrasformandolo grep script_namein grep -e "[s]cript_name".
nmichaels,

3

Elencare i lavori in background utilizzando

# jobs

Quindi selezionare il numero precedente di lavoro ed eseguire

esempio

# fg 1 

inserisci qui la descrizione dell'immagine

porterà in primo piano.

Quindi uccidilo usando CTRL + C o un modo più semplice per trovare il PID dello script usando

ps -aux | grep script_name

inserisci qui la descrizione dell'immagine

Quindi uccidi usando pid

sudo kill -9 pid_number_here

2
La prima opzione non è valida per il mio caso perché chiudo la shell dopo aver dichiarato lo script e penso che il comando jobs funzioni solo per la stessa shell. Inoltre, ho provato la seconda opzione e uccide il processo ma quando faccio pgrep inotifywait, riesco ancora a vederlo come un processo lì.
light94,

i lavori forniranno l'elenco dei lavori anche se chiudi la shell. Comunque il processo finirà, sarà lì.
Babin Lonston,

2
L'ho provato ma non l'ha fatto.
light94,

@ light94 Hai ragione. È successo spesso qui che il jobscomando visualizzava realmente solo i lavori in esecuzione mentre era nella shell. Una volta chiusa una determinata finestra del terminale, l'unico modo per accedervi era via ps(vedi sopra per quello). Quindi è sicuro che non lo stai inventando.
syntaxerror,

2

Puoi usare ps+ grepo pgrepper ottenere il nome del processo / pid ; successivamente utilizzare killall/ pkillper eliminare il nome del processo o utilizzare killper uccidere pid. Tutti i seguenti dovrebbero funzionare.

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

La cosa più importante è che dovresti assicurarti di uccidere solo i processi esatti che speri di uccidere.

pkill/ pgrepcorrisponde a un modello anziché al nome esatto , quindi è più pericoloso; qui -xviene aggiunto per abbinare il nome esatto.

Inoltre, quando si utilizza pgrep/ pkillpotrebbe essere necessario

  • -fper abbinare l'intera riga di comando (come ps auxfa)
  • -a per stampare anche il nome del processo.

Dopo aver eseguito quanto sopra, ottengo un errore di utilizzo in ciascuno. sto copiando e sostituendo lo scriptname. Ottengo l'uso di kill come output
light94

Script_name è in esecuzione? Funziona per me (debian jessie)
Hongxu Chen il

si sta funzionando. e la tua soluzione è quasi la stessa delle soluzioni @cremefraiche di cui sopra, quindi mi aspettavo che funzionasse allo stesso modo: /
light94

Sì, stavo riassumendo in qualche modo i possibili modi per farlo, in particolare aggiungendo pgrep/ pkill. Se ancora si sbaglia, potresti provare pgrepsenza -x. Fondamentalmente non penso che sia un bene uccidere il processo all'interno di un comando di linea senza verificare se altri processi sono abbinati.
Hongxu Chen,

1

Come probabilmente puoi dire, ci sono molti modi per farlo.

Per quanto riguarda il tuo "AGGIORNAMENTO # 2" - in generale, terminare qualsiasi processo in una gerarchia genitore-figlio terminerà generalmente tutti i processi associati. Ma ci sono molte eccezioni a questo. Idealmente, si desidera terminare il 'figlio' finale in un albero di processo, quindi i genitori di questo figlio dovrebbero uscire se non hanno altre attività da eseguire. Ma se uccidi un genitore, il segnale dovrebbe essere trasmesso ai bambini quando il genitore muore e anche i bambini dovrebbero uscire - ma ci sono casi in cui i processi dei bambini possono ignorare il segnale (tramite trappole o meccanismi simili) e possono continuare eseguire un test sarà ereditato dal processo 'init' (o simile). Ma questo argomento del comportamento del processo può diventare complesso e lo lascerò lì ...

Un metodo che mi piace se non voglio usare uno script di controllo (descritto in seguito) è usare l'utility 'screen' per avviare e gestire il processo. Il comando 'schermo' è pieno di funzioni e può richiedere del tempo per padroneggiare. Ti incoraggio a leggere la pagina di manuale "schermo" per una spiegazione completa. Un rapido esempio per avviare un processo in background sarebbe il comando:

schermo -d -m / path / to / program

Questo avvierà "/ path / to / program" all'interno di una sessione 'schermo'.

Puoi vedere la tua sessione in esecuzione con il comando:

schermo -ls

E in qualsiasi momento è possibile riconnettersi al programma in esecuzione con il comando:

schermo -r

E poi basta terminarlo con un ^ C o altro.

Oltre alla bellezza di essere in grado di riconnettersi e disconnettersi dal tuo processo a piacimento è che 'schermo' catturerà qualsiasi stdout () che il tuo programma potrebbe produrre.


Ma la mia preferenza personale in queste materie è quella di avere un programma di controllo che gestisca l'avvio e l'arresto di un processo. Questo può diventare alquanto complicato e richiede alcuni script probabilmente complicati. E come ogni sceneggiatura, ci sono dozzine di buoni modi per farlo. Ho incluso un esempio bash di un metodo che uso abitualmente per avviare e arrestare le applicazioni. Se la tua attività è semplice, puoi inserirla direttamente nello script di controllo oppure puoi fare in modo che questo script di controllo chiami un altro programma esterno. Si noti che questo esempio non è affatto completo in termini di gestione del processo. Ho escluso la possibilità di scenari come: Assicurarsi che lo script non sia già in esecuzione quando si utilizza l'opzione "start", convalidando che il PID in esecuzione è effettivamente il processo che è stato avviato (ad esempio, lo script non ha ' t è morto e un altro processo è stato avviato utilizzando lo stesso PID), e confermando che lo script ha effettivamente risposto (chiuso) alla prima richiesta di "kill". Fare tutti questi controlli può essere complicato e non volevo rendere l'esempio troppo lungo e complesso. Potresti voler modificare l'esempio per esercitarti con gli script della shell.

Salvare il seguente codice in un file chiamato "programctl", renderlo eseguibile con il comando:

chmod 755 programctl

Quindi modifica il file e aggiungi il tuo codice / script nella sezione del caso che inizia con "myscript".

Una volta che tutto è a posto, supponendo che "programctl" sia nella directory corrente, è possibile avviare il programma con:

./programctl start

E basta con:

./programctl stop

Saluti.

#!/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

Ho provato il metodo "schermo" che hai suggerito e funziona magnificamente. Devo ancora provare il secondo metodo. Grazie per la risposta.
light94
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.