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.
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.
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("/")
(oucd /
à 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 .
# 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 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
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.
regardez l'outil démon du paquet libslack:
sur 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 à 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>
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
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &
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
essayez d'exécuter en utilisant & si vous enregistrez ce fichier sous program.sh
vous pouvez utiliser
$. program.sh &