Je me demande s'il existe un meilleur moyen de créer un démon qui attend quelque chose en n'utilisant que sh que:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
En particulier, je me demande s’il est possible de se débarrasser de la boucle tout en laissant la chose écouter les signaux.
Utilisez la fonction démon de votre système, telle que start-stop-daemon .
Sinon, oui, il doit y avoir une boucle quelque part.
Arrière-plan de votre script (./myscript &
) ne le démonalisera 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 utiliser un raccourci pour donner l’impression qu’un script se comporte comme un démon;
Nohup ./myscript 0<&- &>/dev/null &
va faire le travail. Ou, pour capturer stderr et stdout dans un fichier:
Nohup ./myscript 0<&- &> my.admin.log.file &
Cependant, vous devrez peut-être prendre en compte d’autres aspects importants. Par exemple:
chdir("/")
(ou cd /
dans votre script), et branchez-le de manière à ce que le parent quitte, de sorte que le descripteur d'origine soit fermé.umask 0
. Vous pouvez ne pas vouloir dépendre de l'umask de l'appelant du démon.Pour un exemple de script prenant en compte tous ces aspects, voir Mike S 'answer .
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
Certaines des réponses votées par le plus de votes ici manquent certaines parties importantes de ce qui fait d’un démon un démon, par opposition à un simple processus en arrière-plan ou à un processus en 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 this Exécuter le script bash en tant que démon implémente setsid, bien qu'il manque le chdir à root.
La question de l'affiche originale était en réalité plus précise que "Comment créer un processus démon avec bash?", Mais comme le sujet et les réponses traitent de la démonisation des scripts Shell en général, il est important de le préciser (pour les intruseurs détails de la création d’un démon).
Voici mon interprétation d'un script Shell qui se comporterait conformément à la FAQ. Définissez DEBUG sur true
pour afficher une jolie sortie (mais elle se ferme immédiatement plutôt que de se répéter en boucle):
#!/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="$1"; tty="$2"
[[ "$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 $0)
cd /
#### CHILD HERE --------------------------------------------------------------------->
if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again.
shift; tty="$1"; 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 [ "$1" != "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="$1"; 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 sur true
. Remarquez comment les numéros de session et de groupe 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
Cela dépend vraiment de ce que le binaire lui-même va faire.
Par exemple, je veux créer un 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 "$1" in
start)
echo -n "Starting Listener Deamon .... "
startproc -f -p $PIDFILE $LISTENER_BIN
echo "running"
;;
*)
echo "Usage: $0 start"
exit 1
;;
esac
c’est ainsi que nous démarrons le démon (moyen commun à tous les employés de /etc/init.d/)
maintenant, s’agissant de l’auditeur lui-même, Ce doit être une sorte de boucle/alerte ou bien cela déclenchera le script de faire ce que vous voulez. Par exemple, si vous voulez que votre script dorme 10 min .__ et vous réveille et vous demande comment vous allez, vous le ferez avec le
while true ; do sleep 600 ; echo "How are u ? " ; done
Voici le simple auditeur que vous pouvez faire qui écoutera vos commandes Depuis une machine distante et les exécutera 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 pour envoyer des commandes à partir de Shell (ou l'envelopper dans un 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.
Jetez un coup d'œil à l'outil daemon du paquet libslack:
Sous Mac OS X, utilisez un script launchd pour le démon Shell.
Si j'avais un script.sh
et que je voulais l'exécuter depuis bash et le laisser fonctionner même lorsque je voulais fermer ma session 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 aucune entrée, nous utilisons généralement /dev/null
. Donc, la commande serait:
Nohup ./script.sh < /dev/null > ./logFile 2>&1 &
Après cela fermez votre session bash, 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. Bien sûr, si vous voulez l’arrêter, exécutez les mêmes commandes (ps) et kill -9 <PID-OF-YOUR-SCRIPT>
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 existe évidemment des différences avec Nohup
. Pour l'un, il n'y a pas de détachement du parent en premier lieu. De plus, "script.sh" n'hérite pas de l'environnement du parent.
En aucun cas c'est une meilleure alternative. C'est simplement une manière différente (et un peu paresseuse) de lancer des processus en arrière-plan.
P.S. Personnellement, j'ai voté en faveur de la réponse de carlo, qui semble être la plus élégante et fonctionne à la fois en mode terminal et en script interne
Voir le projet Bash Service Manager: https://github.com/reduardo7/bash-service-manager
#!/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="$1" # 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="$1" # Action
echo "* Starting with $action"
}
after-finish() {
local action="$1" # Action
local serviceExitCode=$2 # Service exit code
echo "* Finish with $action. Exit code: $serviceExitCode"
}
action="$1"
serviceName="Example Service"
serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish
$ ./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
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &