La meilleure façon de faire un démon de script shell?

je me demande s'il y a une meilleure façon de faire un démon qui attend quelque chose en utilisant seulement sh than:

#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
  echo "doing stuff"
}

while true; do
  sleep 1000
done

en particulier, je me demande s'il n'y a pas un moyen de se débarrasser de la boucle et de laisser la chose écouter les signaux.

64
demandé sur Shawn J. Goff 2010-08-07 16:04:07

11 réponses

utilisez le démon de votre système, tel que start-stop-daemon .

sinon, oui, il doit y avoir une boucle quelque part.

40
répondu Dennis Williamson 2010-08-07 14:09:32

juste backgrounding votre script ( ./myscript & ) ne le démonisera pas. Voir http://www.faqs.org/faqs/unix-faq/programmer/faq / , section 1.7, qui décrit ce qui est nécessaire pour devenir un démon. Vous devez le déconnecter du terminal pour que SIGHUP ne le tue pas. Vous pouvez prendre un raccourci pour faire apparaître un script comme un démon;

nohup ./myscript 0<&- &>/dev/null &

fera l'affaire. Ou, pour capturer à la fois stderr et stdout dans un fichier:

nohup ./myscript 0<&- &> my.admin.log.file &

cependant, il peut y avoir d'autres aspects importants que vous devez considérer. Par exemple:

  • vous aurez toujours un descripteur de fichier ouvert sur le script, ce qui signifie que le répertoire dans lequel il est monté sera unmountable. Pour être un vrai démon, vous devez chdir("/") (ou cd / à l'intérieur de votre script), et bifurquer pour que le parent sorte, et donc le descripteur original est fermé.
  • Peut-être umask 0 . Vous ne voulez pas dépendre sur le umask de l'appelant du démon.

pour un exemple de script qui tient compte de tous ces aspects, voir Mike S' answer .

104
répondu Ken B 2017-05-23 12:34:15
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981 
(./program.sh &) & 
58
répondu carlo 2010-08-07 15:34:14

certaines des réponses proposées ici manquent certaines parties importantes de ce qui fait qu'un démon est un démon, par opposition à un simple processus d'arrière-plan, ou un processus d'arrière-plan détaché d'un shell.

ce http://www.faqs.org/faqs/unix-faq/programmer/faq / décrit ce qui est nécessaire pour être un démon. Et ce script exécute bash en tant que démon implémente le setsid, bien qu'il manque le chdir à root.

la question de l'affiche originale était en fait plus précise que "comment créer un processus démon en utilisant bash?", mais puisque le sujet et les réponses discutent de la démonisation des scripts shell en général, je pense qu'il est important de le signaler (pour les interlopers comme moi regardant dans les détails de la création d'un démon).

Voici mon Rendu d'un script shell qui se comporterait selon la FAQ. Définir DEBUG à true pour voir la sortie de pretty (mais il existe aussi immédiatement plutôt que de boucler sans fin):

#!/bin/bash
DEBUG=false

# This part is for fun, if you consider shell scripts fun- and I do.
trap process_USR1 SIGUSR1

process_USR1() {
    echo 'Got signal USR1'
    echo 'Did you notice that the signal was acted upon only after the sleep was done'
    echo 'in the while loop? Interesting, yes? Yes.'
    exit 0
}
# End of fun. Now on to the business end of things.

print_debug() {
    whatiam=""; tty=""
    [[ "$tty" != "not a tty" ]] && {
        echo "" >$tty
        echo "$whatiam, PID $$" >$tty
        ps -o pid,sess,pgid -p $$ >$tty
        tty >$tty
    }
}

me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
me_FILE=$(basename "151900920")
cd /

#### CHILD HERE --------------------------------------------------------------------->
if [ "" = "child" ] ; then   # 2. We are the child. We need to fork again.
    shift; tty=""; shift
    $DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty"
    umask 0
    $me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null &
    $DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty
    exit 0
fi

##### ENTRY POINT HERE -------------------------------------------------------------->
if [ "" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts.
    tty=$(tty)
    $DEBUG && print_debug "*** PARENT" "$tty"
    setsid $me_DIR/$me_FILE child "$tty" "$@" &
    $DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty
    exit 0
fi

##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace -------------->
                               # 3. We have been reforked. Go to work.
exec >/tmp/outfile
exec 2>/tmp/errfile
exec 0</dev/null

shift; tty=""; shift

$DEBUG && print_debug "*** DAEMON" "$tty"
                               # The real stuff goes here. To exit, see fun (above)
$DEBUG && [[ "$tty" != "not a tty" ]]  && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty

$DEBUG || {
while true; do
    echo "Change this loop, so this silly no-op goes away." >/dev/null
    echo "Do something useful with your life, young man." >/dev/null
    sleep 10
done
}

$DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty

exit # This may never run. Why is it here then? It's pretty.
     # Kind of like, "The End" at the end of a movie that you
     # already know is over. It's always nice.
La sortie

ressemble à ceci lorsque DEBUG est défini à true . Notez comment les numéros d'identification de groupe de session et de processus (SESS, PGID) changent:

<shell_prompt>$ bash blahd

*** PARENT, PID 5180
  PID  SESS  PGID
 5180  1708  5180
/dev/pts/6
PARENT OUT
<shell_prompt>$ 
*** CHILD, NEW SESSION, NEW PGID, PID 5188
  PID  SESS  PGID
 5188  5188  5188
not a tty
CHILD OUT

*** DAEMON, PID 5198
  PID  SESS  PGID
 5198  5188  5188
not a tty
NOT A REAL DAEMON. NOT RUNNING WHILE LOOP.
DAEMON OUT
57
répondu Mike S 2017-05-23 12:09:56

cela dépend vraiment de ce que va faire le binaire lui-même.

par exemple je veux créer quelque auditeur.

le démon de départ est une tâche simple:

lis_deamon:

#!/bin/bash

# We will start the listener as Deamon process
#    
LISTENER_BIN=/tmp/deamon_test/listener
test -x $LISTENER_BIN || exit 5
PIDFILE=/tmp/deamon_test/listener.pid

case "" in
      start)
            echo -n "Starting Listener Deamon .... "
            startproc -f -p $PIDFILE $LISTENER_BIN
            echo "running"
            ;;
          *)
            echo "Usage: "151900920" start"
            exit 1
            ;;
esac

c'est la façon dont nous lancer le démon (voie commune pour tous /etc/init.d/ personnel)

maintenant comme pour l'auditeur lui-même, Il doit s'agir d'une sorte de boucle/alerte ou bien cela déclenchera le script de faire ce que tu veux. Par exemple, si vous voulez que votre script dorme 10 min et réveillez-vous et demandez-vous comment vous faites u va le faire avec le

while true ; do sleep 600 ; echo "How are u ? " ; done

Voici l'auditeur simple que vous pouvez faire qui écoutera pour votre commandes de la machine distante et les exécuter sur local:

auditeur:

#!/bin/bash

# Starting listener on some port
# we will run it as deamon and we will send commands to it.
#
IP=$(hostname --ip-address)
PORT=1024
FILE=/tmp/backpipe
count=0
while [ -a $FILE ] ; do #If file exis I assume that it used by other program
  FILE=$FILE.$count
  count=$(($count + 1))
done

# Now we know that such file do not exist,
# U can write down in deamon it self the remove for those files
# or in different part of program

mknod $FILE p

while true ; do 
  netcat -l -s $IP -p $PORT < $FILE |/bin/bash > $FILE
done
rm $FILE

donc pour le démarrer:/tmp/deamon_test / listener start

et d'envoyer des commandes depuis shell (ou l'envelopper de script) :

test_host#netcat 10.184.200.22 1024
uptime
 20:01pm  up 21 days  5:10,  44 users,  load average: 0.62, 0.61, 0.60
date
Tue Jan 28 20:02:00 IST 2014
 punt! (Cntrl+C)

J'espère que cela aidera.

4
répondu Artem 2014-01-29 01:04:51

regardez l'outil démon du paquet libslack:

http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script /

sur Mac OS X utilisez un script launchd pour le démon shell.

1
répondu timo 2010-08-07 14:03:50

si j'avais un script.sh et que je voulais l'exécuter à partir de bash et le laisser tourner même quand je veux fermer ma session de bash, Je combinerais nohup et & à la fin.

exemple: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &

inputFile.txt peut être n'importe quel fichier. Si votre fichier n'a pas d'entrée, alors nous utilisons habituellement /dev/null . Donc la commande serait:

nohup ./script.sh < /dev/null > ./logFile 2>&1 &

après que fermer votre bash session, ouvrez un autre terminal et exécutez: ps -aux | egrep "script.sh" et vous verrez que votre script est toujours en cours d'exécution en arrière-plan. De cource,si vous voulez l'arrêter puis exécuter la même commande (ps) et kill -9 <PID-OF-YOUR-SCRIPT>

1
répondu noName 2015-10-01 19:43:01

See Bash Service Manager project: https://github.com/reduardo7/bash-service-manager

exemple de mise en œuvre

#!/usr/bin/env bash

export PID_FILE_PATH="/tmp/my-service.pid"
export LOG_FILE_PATH="/tmp/my-service.log"
export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log"

. ./services.sh

run-script() {
  local action="" # Action

  while true; do
    echo "@@@ Running action '${action}'"
    echo foo
    echo bar >&2

    [ "$action" = "run" ] && return 0
    sleep 5
    [ "$action" = "debug" ] && exit 25
  done
}

before-start() {
  local action="" # Action

  echo "* Starting with $action"
}

after-finish() {
  local action="" # Action
  local serviceExitCode= # Service exit code

  echo "* Finish with $action. Exit code: $serviceExitCode"
}

action=""
serviceName="Example Service"

serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish

exemple d'utilisation

$ ./example-service
# Actions: [start|stop|restart|status|run|debug|tail(-[log|error])]

$ ./example-service start
# Starting Example Service service...

$ ./example-service status
# Serive Example Service is runnig with PID 5599

$ ./example-service stop
# Stopping Example Service...

$ ./example-service status
# Service Example Service is not running
0
répondu Eduardo Cuomo 2017-06-03 01:22:28

$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &

0
répondu Congbin Guo 2018-03-08 22:44:17

comme beaucoup de réponses, celle-ci n'est pas une" vraie "démonisation mais plutôt une alternative à l'approche nohup .

echo "script.sh" | at now

il y a évidemment des différences par rapport à l'utilisation de nohup . Pour l'un, il n'y a pas de séparation du parent en premier lieu. Aussi "script.sh" n'hérite pas de l'environnement du parent.

il ne s'agit en aucun cas d'une meilleure alternative. Il s'agit simplement d'une manière différente (et quelque peu paresseuse) de lancer des processus dans fond.

P. S .. j'ai personnellement rejeté la réponse de carlo car elle semble être la plus élégante et fonctionne à la fois à partir de terminaux et de scripts internes

0
répondu oᴉɹǝɥɔ 2018-09-20 15:54:37

essayez d'exécuter en utilisant & si vous enregistrez ce fichier sous program.sh

vous pouvez utiliser

$. program.sh &
-3
répondu jasimmk 2012-06-15 13:09:52