wake-up-neo.net

Der beste Weg, um einen Shell-Skript-Daemon zu erstellen?

Ich frage mich, ob es einen besseren Weg gibt, einen Dämon zu erstellen, der nur mit sh auf etwas wartet, als:

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

while true; do
  sleep 1000
done

Insbesondere frage ich mich, ob es eine Möglichkeit gibt, die Schleife loszuwerden und das Ding trotzdem auf die Signale hören zu lassen.

67
Shawn J. Goff

Verwenden Sie die Daemon-Funktion Ihres Systems, z. B. start-stop-daemon .

Ansonsten, ja, irgendwo muss es eine Schleife geben.

44

Wenn Sie Ihr Skript (./myscript &) nur als Hintergrund verwenden, wird es nicht dämonisiert. Siehe http://www.faqs.org/faqs/unix-faq/programmer/faq/ , Abschnitt 1.7. Dort wird beschrieben, was erforderlich ist, um ein Dämon zu werden. Sie müssen es vom Terminal trennen, damit SIGHUP es nicht beendet. Sie können eine Verknüpfung verwenden, damit ein Skript wie ein Dämon wirkt.

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

wird die Arbeit machen. Oder, um stderr und stdout in einer Datei zu erfassen:

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

Es gibt jedoch möglicherweise weitere wichtige Aspekte, die Sie berücksichtigen müssen. Zum Beispiel:

  • Das Skript verfügt weiterhin über eine Dateibeschreibung, dh das Verzeichnis, in das es eingebunden ist, kann nicht bereitgestellt werden. Um ein echter Daemon zu sein, sollten Sie chdir("/") (oder cd / innerhalb Ihres Skripts) und verzweigen, so dass das übergeordnete Element beendet wird und der ursprüngliche Deskriptor geschlossen wird.
  • Vielleicht umask 0 ausführen. Möglicherweise möchten Sie sich nicht auf die Umask des Aufrufers des Dämons verlassen.

Ein Beispiel für ein Skript, das all diese Aspekte berücksichtigt, finden Sie unter Mike S 'answer .

106
Ken B
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981 
(./program.sh &) & 
60
carlo

Bei einigen der Antworten mit Top-Up-Befehlen fehlen einige wichtige Teile dessen, was einen Daemon zu einem Daemon macht, im Gegensatz zu einem Hintergrundprozess oder einem Hintergrundprozess, der von einer Shell getrennt ist.

Diese http://www.faqs.org/faqs/unix-faq/programmer/faq/ beschreibt, was notwendig ist, um ein Dämon zu sein. Und dieses Bash-Skript als Daemon ausführen implementiert die Setsid, obwohl das chdir-Root-Verzeichnis fehlt.

Die Frage des ursprünglichen Posters war eigentlich genauer als "Wie erstelle ich einen Daemon-Prozess mit bash?", Aber da das Thema und die Antworten die Daemonisierung von Shell-Skripts im Allgemeinen behandeln, ist es meiner Meinung nach wichtig, darauf hinzuweisen (für Eindringlinge wie mich, die in die feine Details zum Erstellen eines Daemons).

Hier ist meine Wiedergabe eines Shell-Skripts, das sich gemäß den FAQ verhält. Setzen Sie DEBUG auf true, um eine hübsche Ausgabe zu sehen (sie wird jedoch auch sofort beendet und nicht endlos wiederholt):

#!/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" "[email protected]" </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" "[email protected]" &
    $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.

Die Ausgabe sieht folgendermaßen aus, wenn DEBUG auf true gesetzt ist. Beachten Sie, wie sich die Sitzungs- und Prozessgruppen-ID (SESS, PGID) ändert:

<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
60
Mike S

Es hängt wirklich davon ab, was die Binärdatei selbst tun wird.

Zum Beispiel möchte ich einen Listener erstellen.

Der Start-Daemon ist eine einfache Aufgabe:

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

so starten wir den Daemon (allgemeiner Weg für alle Mitarbeiter /etc/init.d/)

wie für den Listener selbst.... Es muss sich um eine Art Schleife/Alarm handeln, ansonsten wird das Skript .__ ausgelöst, um das zu tun, was Sie wollen. Wenn Sie zum Beispiel möchten, dass Ihr Skript 10 Minuten lang im Ruhezustand ist, und aufwachen und Sie fragen, wie es Ihnen geht, tun Sie dies mit 

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

Hier ist der einfache Listener, den Sie tun können, um Ihre Befehle von einem entfernten Rechner zu überwachen und auf lokal auszuführen:

zuhörer:

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

So starten Sie es:/tmp/deamon_test/listener start

und um Befehle von der Shell zu senden (oder in ein Skript zu packen):

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)

Hoffe das wird helfen.

4
Artem

Schauen Sie sich das Daemon-Tool aus dem libslack-Paket an:

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

Verwenden Sie unter Mac OS X ein Startskript für den Shell-Daemon.

1
timo

Wenn ich einen script.sh hätte und ich wollte es von bash ausführen und laufen lassen, selbst wenn ich meine bash-Sitzung beenden möchte, dann würde ich Nohup und & am Ende kombinieren.

beispiel: Nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &

inputFile.txt kann eine beliebige Datei sein. Wenn Ihre Datei keine Eingabe hat, verwenden wir normalerweise /dev/null. Der Befehl wäre also:

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

Schließen Sie danach Ihre bash-Sitzung, öffnen Sie ein anderes Terminal und führen Sie Folgendes aus: ps -aux | egrep "script.sh" und Sie werden sehen, dass Ihr Skript noch im Hintergrund ausgeführt wird. Wenn Sie es stoppen wollen, führen Sie den gleichen Befehl (ps) und kill -9 <PID-OF-YOUR-SCRIPT> aus.

1
noName

Wie viele Antworten ist dies keine "echte" Daemonisierung, sondern eine Alternative zu Nohup.

echo "script.sh" | at now

Es gibt offensichtlich Unterschiede bei der Verwendung von Nohup. Zum einen gibt es überhaupt keine Trennung vom Elternteil. Auch "script.sh" erbt die Umgebung der Eltern nicht.

Dies ist auf keinen Fall eine bessere Alternative. Es ist einfach eine andere (und etwas träge) Art, Prozesse im Hintergrund zu starten.

P.S. Ich persönlich habe die Antwort von carlo bestätigt, da sie die eleganteste zu sein scheint und sowohl aus Terminal- als auch aus Inskriptions-Skripts funktioniert

0
oᴉɹǝɥɔ

Siehe Bash Service Manager - Projekt: https://github.com/reduardo7/bash-service-manager

Implementierungsbeispiel

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

Verwendungsbeispiel

$ ./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
Eduardo Cuomo

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

0
Congbin Guo