Come posso demonizzare uno script arbitrario in unix?


93

Vorrei un demone che possa trasformare uno script o un comando generico e arbitrario in un demone .

Ci sono due casi comuni che vorrei trattare:

  1. Ho uno script che dovrebbe essere eseguito per sempre. Se mai muore (o al riavvio), riavvialo. Non lasciare mai che ci siano due copie in esecuzione contemporaneamente (rileva se una copia è già in esecuzione e non avviarla in quel caso).

  2. Ho un semplice script o un comando da riga di comando che vorrei continuare a eseguire ripetutamente per sempre (con una breve pausa tra le esecuzioni). Anche in questo caso, non consentire l'esecuzione simultanea di due copie dello script.

Ovviamente è banale scrivere un ciclo "while (true)" attorno allo script nel caso 2 e quindi applicare una soluzione per il caso 1, ma una soluzione più generale risolverà direttamente il caso 2 poiché si applica allo script nel caso 1 come bene (potresti volere solo una pausa più breve o nessuna pausa se la sceneggiatura non è destinata a morire mai (ovviamente se la sceneggiatura non muore mai, allora la pausa non ha importanza)).

Si noti che la soluzione non dovrebbe comportare, ad esempio, l'aggiunta di codice di blocco dei file o registrazione PID agli script esistenti.

Più specificamente, vorrei un programma "daemonize" che posso eseguire come

% daemonize myscript arg1 arg2

o, ad esempio,

% daemonize 'echo `date` >> /tmp/times.txt'

che manterrebbe un elenco crescente di date aggiunto a times.txt. (Nota che se l'argomento (i) per demonizzare è uno script che viene eseguito per sempre come nel caso 1 sopra, allora daemonize farà comunque la cosa giusta, riavviandolo quando necessario.) Potrei quindi inserire un comando come sopra nel mio .login e / o cron ogni ora o minuziosamente (a seconda di quanto fossi preoccupato che morisse inaspettatamente).

NB: Lo script daemonize dovrà ricordare la stringa di comando che sta demonizzando in modo che, se la stessa stringa di comando viene nuovamente daemonizzata, non avvierà una seconda copia.

Inoltre, la soluzione dovrebbe funzionare idealmente sia su OS X che su Linux, ma le soluzioni per l'una o l'altra sono benvenute.

EDIT: Va bene se devi invocarlo con sudo daemonize myscript myargs.

(Se sto pensando a tutto questo in modo sbagliato o ci sono soluzioni parziali rapide e sporche, mi piacerebbe sentirlo anche io.)


PS: Nel caso sia utile, ecco una domanda simile specifica per Python.

E questa risposta a una domanda simile ha quello che sembra essere un utile idioma per una demonizzazione rapida e sporca di uno script arbitrario:


1
Vedi serverfault.com/questions/311593/… per una versione shell pura
w00t

Risposte:


90

Puoi demonizzare qualsiasi eseguibile in Unix usando nohup e l'operatore &:

nohup yourScript.sh script args&

Il comando nohup ti consente di chiudere la sessione della shell senza che questo interrompa lo script, mentre & posiziona lo script in background in modo da ottenere un prompt della shell per continuare la sessione. L'unico problema minore con questo è l'uscita standard e l'errore standard vengono entrambi inviati a ./nohup.out, quindi se avvii diversi script in questo maniero il loro output sarà intrecciato. Un comando migliore sarebbe:

nohup yourScript.sh script args >script.out 2>script.error&

Questo invierà lo standard al file di tua scelta e l'errore standard a un file diverso di tua scelta. Se desideri utilizzare un solo file sia per l'uscita standard che per l'errore standard, puoi utilizzare questo:

nohup yourScript.sh script args >script.out 2>&1 &

Il 2> & 1 dice alla shell di reindirizzare lo standard error (descrittore di file 2) allo stesso file di standard out (descrittore di file 1).

Per eseguire un comando solo una volta e riavviarlo se muore puoi usare questo script:

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

Il primo argomento è il nome del file pid da utilizzare. Il secondo argomento è il comando. E tutti gli altri argomenti sono gli argomenti del comando.

Se assegni il nome a questo script restart.sh, è così che lo chiameresti:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

Eccezionale; grazie. Mi chiedo se dovrebbe anche avere un'opzione per un ritardo nel riavvio. O forse è meglio usarlo insieme a questo: stackoverflow.com/questions/555116/…
dreeves

4
Questo gestisce solo SIGHUP, ci sono altri segnali (normalmente) fatali che dovrebbero essere gestiti.
Tim Post

Un altro modo per migliorare questo script è probabilmente quello di trovare una buona posizione per posizionare $ PIDFILE da solo piuttosto che richiedere che sia specificato come argomento. Non si pulisce nemmeno dopo se stesso! (che dovrebbe essere semplice con a trap EXIT)
Steven Lu

Inoltre, considera che l'uso di <in testè un confronto ASCII non un confronto di numeri interi. Potrebbe ancora funzionare, ma potrebbe portare a bug.
Steven Lu

Ho pubblicato le mie correzioni a questo script qui .
Steven Lu

33

Mi scuso per la lunga risposta (si prega di vedere i commenti su come la mia risposta inchioda le specifiche). Sto cercando di essere esauriente, quindi hai il miglior vantaggio possibile. :-)

Se sei in grado di installare programmi (hai accesso root) e sei disposto a fare una sola volta il legwork per configurare il tuo script per l'esecuzione daemon (cioè, più complicato che specificare semplicemente gli argomenti della riga di comando da eseguire sulla riga di comando, ma solo una volta per servizio), ho un modo più robusto.

Implica l'utilizzo di daemontools . Il resto del post descrive come configurare i servizi usando daemontools.

Configurazione iniziale

  1. Segui le istruzioni in Come installare daemontools . Alcune distribuzioni (ad esempio, Debian, Ubuntu) hanno già dei pacchetti per esso, quindi usalo.
  2. Crea una directory chiamata /service. Il programma di installazione dovrebbe averlo già fatto, ma è sufficiente verificare o se si installa manualmente. Se non ti piace questa posizione, puoi cambiarla nel tuo svscanbootscript, anche se la maggior parte degli utenti di daemontools è abituata a usarla /servicee si confonderà se non la usi.
  3. Se stai usando Ubuntu o un'altra distribuzione che non usa lo standard init(cioè, non usa /etc/inittab), dovrai usare il preinstallato inittabcome base per organizzare la svscanbootchiamata init. Non è difficile, ma devi sapere come configurare il initsistema utilizzato dal tuo sistema operativo. svscanbootè uno script che chiama svscan, che svolge il lavoro principale di ricerca di servizi; viene chiamato da initquindi initprovvederà a riavviarlo se muore per qualsiasi motivo.

Configurazione per servizio

  1. Ogni servizio necessita di una directory di servizi , che memorizza le informazioni di pulizia sul servizio. Puoi anche creare una posizione per ospitare queste directory di servizi in modo che siano tutte in un unico posto; di solito lo uso /var/lib/svscan, ma qualsiasi nuova posizione andrà bene.
  2. Di solito uso uno script per impostare la directory del servizio, per risparmiare un sacco di lavoro manuale ripetitivo. per esempio,

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"

    dove some-service-nameè il nome che si desidera dare al servizio, userè l'utente con cui eseguire quel servizio ed loguserè l'utente con cui eseguire il logger. (La registrazione è spiegata solo un po '.)

  3. Il tuo servizio deve essere eseguito in primo piano . Se il tuo programma è in background per impostazione predefinita, ma ha un'opzione per disabilitarlo, fallo. Se il tuo programma è in background senza un modo per disabilitarlo, continua a leggere fghack, anche se questo ha un compromesso: non puoi più controllare il programma usando svc.
  4. Modifica lo runscript per assicurarti che stia facendo quello che vuoi. Potrebbe essere necessario effettuare una sleepchiamata in alto, se si prevede che il servizio venga chiuso frequentemente.
  5. Quando tutto è impostato correttamente, crea un /servicecollegamento simbolico che punta alla directory del servizio. (Non inserire le directory dei servizi direttamente all'interno /service; rende più difficile rimuovere il servizio svscandall'orologio di.)

Registrazione

  1. Il modo di logging daemontools consiste nel far scrivere al servizio i messaggi di log sullo standard output (o errore standard, se si utilizzano script generati con mkservice); svscansi occupa di inviare i messaggi di log al servizio di logging.
  2. Il servizio di registrazione prende i messaggi di registro dall'input standard. Lo script del servizio di registrazione generato da mkservicecreerà file di registro a rotazione automatica con timestamp nella log/maindirectory. Viene chiamato il file di registro corrente current.
  3. Il servizio di registrazione può essere avviato e arrestato indipendentemente dal servizio principale.
  4. Il tai64nlocaltrasferimento dei file di registro tradurrà i timestamp in un formato leggibile dall'uomo. (TAI64N è un timestamp atomico a 64 bit con un conteggio di nanosecondi.)

Servizi di controllo

  1. Utilizzare svstatper ottenere lo stato di un servizio. Notare che il servizio di registrazione è indipendente e ha il proprio stato.
  2. Puoi controllare il tuo servizio (avvio, arresto, riavvio, ecc.) Utilizzando svc. Ad esempio, per riavviare il servizio, utilizzare svc -t /service/some-service-name; -tsignifica "invia SIGTERM".
  3. Altri segnali disponibili includono -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) e -k( SIGKILL).
  4. Per disattivare il servizio, usa -d. È inoltre possibile impedire l'avvio automatico di un servizio all'avvio creando un file denominato downnella directory del servizio.
  5. Per avviare il servizio, usa -u. Questo non è necessario a meno che non lo abbiate disattivato in precedenza (o non lo avete impostato per l'avvio automatico).
  6. Per chiedere al supervisore di uscire, utilizzare -x; solitamente utilizzato anche con -dper terminare il servizio. Questo è il modo usuale per consentire la rimozione di un servizio, ma devi prima scollegare il servizio /service, altrimenti svscanriavvierai il supervisore. Inoltre, se hai creato il tuo servizio con un servizio di registrazione ( mkservice -l), ricordati di uscire anche dal supervisore di registrazione (ad esempio svc -dx /var/lib/svscan/some-service-name/log) prima di rimuovere la directory del servizio.

Sommario

Professionisti:

  1. daemontools fornisce un modo a prova di proiettile per creare e gestire i servizi. Lo uso per i miei server e lo consiglio vivamente.
  2. Il suo sistema di registrazione è molto robusto, così come la funzione di riavvio automatico del servizio.
  3. Poiché avvia i servizi con uno script di shell che scrivi / ottimizzi, puoi personalizzare il tuo servizio come preferisci.
  4. Potenti strumenti di controllo dei servizi: puoi inviare quasi tutti i segnali a un servizio e puoi portare i servizi su e giù in modo affidabile.
  5. Ai tuoi servizi è garantito un ambiente di esecuzione pulito: verranno eseguiti con lo stesso ambiente, limiti di processo, ecc., Di ciò che initfornisce.

Contro:

  1. Ogni servizio richiede un po 'di configurazione. Per fortuna, questo deve essere fatto solo una volta per servizio.
  2. I servizi devono essere configurati per essere eseguiti in primo piano. Inoltre, per ottenere i migliori risultati, dovrebbero essere impostati per accedere allo standard output / standard error, piuttosto che a syslog o ad altri file.
  3. Curva di apprendimento ripida se non conosci il modo di fare le cose daemontools. È necessario riavviare i servizi utilizzando svce non è possibile eseguire direttamente gli script di esecuzione (poiché non sarebbero quindi sotto il controllo del supervisore).
  4. Molti file di pulizia e molti processi di pulizia. Ogni servizio necessita della propria directory di servizio e ogni servizio utilizza un processo supervisore per riavviare automaticamente il servizio se muore. (Se si dispone di molti servizi, vedrete un sacco di superviseprocessi nella vostra tabella dei processi.)

In equilibrio, penso che daemontools sia un ottimo sistema per le tue esigenze. Accolgo con favore qualsiasi domanda su come configurarlo e mantenerlo.


In che modo la mia risposta definisce le specifiche: 1. Devi impostare i servizi, quindi finché non imposti duplicati (e fintanto che il tuo servizio non è in background), non si verificheranno duplicati. 2. supervise, il supervisore, si occupa di riavviare l'eventuale servizio che esce. Attende un secondo tra i riavvii; se questo non è abbastanza per te, addormentati all'inizio dello script di esecuzione del servizio.
Chris Jester-Young

2a. superviseè a sua volta supportato da svscan, quindi se un supervisore muore, verrà riavviato. 2b. svscanè supportato da init, che si riavvierà automaticamente svscanse necessario. 2c. Se initmuori per qualsiasi motivo, sei fregato comunque. :-P
Chris Jester-Young

Per rispondere ad altre domande sulla pulizia, il sistema daemontools non utilizza i file PID, poiché possono diventare obsoleti. Tutte le informazioni sul processo vengono invece conservate dal supervisore che supporta un determinato servizio. Il supervisore mantiene una serie di file (e FIFO) nella directory dei servizi che gli strumenti apprezzano svstate con cui svcpossono lavorare.
Chris Jester-Young

3
Dovremmo avere più post come questo in SO e in rete in generale. Non solo una ricetta su come ottenere l'effetto desiderato ma una che si prende la briga di spiegare le ricette. Perché non posso votare a favore più di una volta? : |
skytreader

12

Penso che potresti voler provare start-stop-daemon(8). Controlla gli script in /etc/init.dqualsiasi distribuzione Linux per esempi. Può trovare i processi avviati tramite riga di comando invocata o file PID, quindi soddisfa tutti i requisiti tranne essere un watchdog per lo script. Ma puoi sempre avviare un altro script watchdog daemon che riavvia lo script se necessario.


Non esiste ancora alcun demone start-stop in Fedora, quindi gli script che dipendono da esso non sono portabili. Vedi: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

Solo un avvertimento per gli utenti OSX: neanche start-stop-daemonlì (a partire dalla 10.9).
mklement0

@ mklement0 Beh ... molto è cambiato in quasi 5 anni.
Alex B

Come vola il tempo. start-stop-daemonè ancora vivo e vegeto su Linux, però; ma dopo aver letto la risposta stackoverflow.com/a/525406/45375 mi resi conto che OSX fa la sua cosa: launchd.
mklement0

12

Dovresti dare un'occhiata a daemonize . Permette di rilevare la seconda copia (ma utilizza un meccanismo di blocco dei file). Inoltre funziona su diverse distribuzioni UNIX e Linux.

Se è necessario avviare automaticamente l'applicazione come daemon, è necessario creare lo script di inizializzazione appropriato.

Puoi utilizzare il seguente modello:

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

1
Sembra un candidato per la risposta corretta. Soprattutto considerando il suo "controllo di istanza singola".
Martin Wickman

Questa potrebbe essere la migliore risposta possibile - non ne sono sicuro - ma se pensi che lo sia, puoi anche includere una spiegazione del motivo per cui le specifiche che ho fornito nella domanda sono sbagliate?
dreeves il

Non mi piace la killprocparte di arresto: se avessi un processo che, ad esempio, è stato eseguito java, killproccauserà la morte anche di tutti gli altri processi Java.
Chris Jester-Young

1
Da /etc/rc.d/init.d/functions, daemonize avvia il binario da una nuova shell: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*Quindi dubito che demonizzerà qualsiasi cosa ...
mbonnin

1
So che è vecchio, ma per chiunque lo trovi più tardi ... è corretto. "daemon" come definito in /etc/init.d/functions non lo fa realtà daemonizzato per te. E 'solo un wrapper per fare cgroups, verifica se il processo è già in esecuzione, impostare un utente, impostare curato e ulimit valori, ecc Essa non demonizzare il processo per voi. È ancora il tuo lavoro. :)
jakem

7

In alternativa al già citato daemonizee daemontools, c'è il comando demone del pacchetto libslack.

daemon è abbastanza configurabile e si preoccupa di tutte le cose noiose dei demoni come il riavvio automatico, la registrazione o la gestione dei pidfile.


5

Se stai usando OS X in particolare, ti suggerisco di dare un'occhiata a come funziona launchd. Verificherà automaticamente che lo script sia in esecuzione e lo riavvierà se necessario. Include anche tutti i tipi di funzionalità di pianificazione, ecc. Dovrebbe soddisfare sia i requisiti 1 che 2.

Per garantire che possa essere eseguita una sola copia dello script, è necessario utilizzare un file PID. Generalmente scrivo un file in /var/run/.pid che contiene un PID dell'istanza in esecuzione corrente. se il file esiste quando il programma viene eseguito, controlla se il PID nel file è effettivamente in esecuzione (il programma potrebbe essersi arrestato in modo anomalo o altrimenti dimenticato di eliminare il file PID). Se lo è, interrompi. In caso contrario, avviare l'esecuzione e sovrascrivere il file PID.


5

Daemontools ( http://cr.yp.to/daemontools.html ) è un insieme di utility piuttosto hard-core usate per fare questo, scritte da dj bernstein. L'ho usato con un certo successo. La parte fastidiosa è che nessuno degli script restituisce risultati visibili quando vengono eseguiti, solo codici di ritorno invisibili. Ma una volta avviato è a prova di proiettile.


Sì, stavo per scrivere una voce che utilizza anche daemontools. Scriverò il mio post, perché spero di essere molto più completo con la mia risposta e spero di ottenere la taglia in questo modo. Vedremo. :-)
Chris Jester-Young

3

Per prima cosa ottieni createDaemon()da http://code.activestate.com/recipes/278731/

Quindi il codice principale:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

Ooh, grazie! Vuoi renderlo leggermente più generale in modo da poter fare "daemonize foo arg1 arg2" così come "daemonize 'foo arg1 arg2'"?
dreeves l'

Ok, ora unirà gli argomenti - tuttavia dovrai cambiarlo se vuoi avere spazi all'interno dei tuoi argomenti.
Douglas Leeder

Grazie Douglas! C'è un grosso difetto però: l'esecuzione di "daemonize foo" due volte avvia l'esecuzione di due copie di foo.
dreeves l'

Potresti aggiungere del codice di registrazione PID, ma potrebbe essere meglio eseguire lo script solo una volta ...
Douglas Leeder,

Penso che sia fondamentale per l'intero concetto di wrapper "daemonize". (Ad esempio, potrei quindi cron ogni ora o minuziosamente per assicurarmi che sia sempre in esecuzione.) Sto pensando a questo sbagliato? CreateDaemon lo garantisce già in qualche modo? E dopo il riavvio?
dreeves il

1

Questa è una versione funzionante completa di un esempio che puoi copiare in una directory vuota e provare (dopo aver installato le dipendenze CPAN, che sono Getopt :: Long , File :: Spec , File :: Pid e IPC :: System: : Semplice - tutti abbastanza standard e sono altamente raccomandati per qualsiasi hacker: puoi installarli tutti in una volta con cpan <modulename> <modulename> ...).


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

esempio.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

Ora puoi richiamare l'esempio sopra con: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3e il file pidfileverrà creato e vedrai l'output:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

Credo che non sia esattamente conforme alle specifiche, se ho capito bene. Nella tua soluzione (grazie, btw!) Il programma che vuoi demonizzare deve essere modificato per scrivere il suo PID nel file PID. Spero in un'utilità in grado di demonizzare uno script arbitrario.
dreeves il

@dreeves: sì, ma ci sono due modi per aggirare questo: 1. lo script invocato da keepAlive.pl (ad esempio example.pl) potrebbe semplicemente essere un wrapper per eseguire il programma reale, oppure 2. keepAlive.pl potrebbe analizzare la tabella di processi di sistema attivi (con Proc :: ProcessTable di CPAN) per tentare di trovare il processo rilevante e il suo pid).
Ether

1

Potresti anche provare Monit . Monit è un servizio che monitora e riporta su altri servizi. Sebbene sia utilizzato principalmente come un modo per notificare (tramite e-mail e sms) sui problemi di runtime, può anche fare ciò che la maggior parte degli altri suggerimenti qui hanno sostenuto. Può avviare e arrestare automaticamente i programmi, inviare e-mail, avviare altri script e mantenere un registro dell'output che è possibile raccogliere. Inoltre, ho trovato che è facile da installare e mantenere poiché c'è una solida documentazione.


1

Potresti provare a immortale È un supervisore multipiattaforma * nix (indipendente dal sistema operativo).

Per una rapida prova su macOS:

brew install immortal

Nel caso tu stia usando FreeBSD dai port o usando pkg:

pkg install immortal

Per Linux scaricando i binari precompilati o dalla fonte: https://immortal.run/source/

Puoi usarlo in questo modo:

immortal -l /var/log/date.log date

O da un file YAML di configurazione che offre più opzioni, ad esempio:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

Se desideri mantenere anche l'output dello standard error in un file separato, potresti usare qualcosa come:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

0

Ho apportato una serie di miglioramenti all'altra risposta .

  1. stdout in questo scriptècostruito puramente da stdout proveniente dal suo figlio A MENO CHE non esca a causa del rilevamento che il comando è già in esecuzione
  2. ripulisce dopo il suo pidfile quando viene terminato
  3. periodo di timeout configurabile opzionale (accetta qualsiasi argomento numerico positivo, invia a sleep)
  4. prompt di utilizzo -h
  5. l'esecuzione di comandi arbitrari, piuttosto che l'esecuzione di un singolo comando. L'ultimo argomento O gli argomenti rimanenti (se più di un ultimo argomento) vengono inviati eval, quindi puoi costruire qualsiasi tipo di script di shell come una stringa da inviare a questo script come ultimo argomento (o argomenti finali) per demonizzare
  6. confronti del conteggio degli argomenti eseguiti con -ltinvece di<

Ecco lo script:

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

Utilizzo:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

Fai attenzione che se esegui questo script da directory diverse, potrebbe utilizzare diversi pidfile e non rilevare alcuna istanza in esecuzione esistente. Poichéèprogettato per eseguire e riavviare comandi temporanei forniti tramite un argomento non c'è modo di sapere se qualcosa è già stato avviato, perché chi può dire se si tratta dello stesso comando o no? Per migliorare questa applicazione dell'esecuzione di una singola istanza di qualcosa, è necessaria una soluzione specifica per la situazione.

Inoltre, affinché funzioni come un demone appropriato, è necessario utilizzare (come minimo) nohup come menzionato dall'altra risposta. Non ho fatto alcuno sforzo per fornire alcuna resilienza ai segnali che il processo potrebbe ricevere.

Un altro punto di cui prendere nota è che uccidere questo script (se è stato chiamato da un altro script che viene ucciso, o con un segnale) potrebbe non riuscire a uccidere il bambino, specialmente se il bambino è ancora un altro script. Non sono sicuro del motivo, ma sembra che sia qualcosa legato al modo in cui evalfunziona, il che è misterioso per me. Quindi potrebbe essere prudente sostituire quella riga con qualcosa che accetta solo un singolo comando come nell'altra risposta.

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.