Linux: Schedule-Befehl, der nach dem Neustart einmal ausgeführt werden soll (RunOnce-Entsprechung)

26

Ich möchte einen Befehl planen, der nach dem Neustart auf einer Linux-Box ausgeführt wird. Ich weiß, wie das geht, damit der Befehl nach jedem Neustart mit einem @rebootcrontab-Eintrag konsistent ausgeführt wird. Ich möchte jedoch, dass der Befehl nur einmal ausgeführt wird. Nachdem es ausgeführt wurde, sollte es aus der Warteschlange der auszuführenden Befehle entfernt werden. Ich bin im Wesentlichen auf der Suche nach einem Linux-Äquivalent zu RunOnce in der Windows-Welt.

Falls es darauf ankommt:

$ uname -a
Linux devbox 2.6.27.19-5-default #1 SMP 2009-02-28 04:40:21 +0100 x86_64 x86_64 x86_64 GNU/Linux
$ bash --version
GNU bash, version 3.2.48(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat /etc/SuSE-release
SUSE Linux Enterprise Server 11 (x86_64)
VERSION = 11
PATCHLEVEL = 0

Gibt es eine einfache, skriptfähige Möglichkeit, dies zu tun?

Christopher Parker
quelle
1
RunOnce ist ein Artefakt von Windows, das durch Probleme beim Abschluss der Konfiguration vor einem Neustart verursacht wird. Gibt es einen Grund, warum Sie Ihr Skript vor dem Neustart nicht ausführen können? Die obige Lösung scheint ein vernünftiger Klon von RunOnce zu sein.
BillThor

Antworten:

38

Erstellen Sie einen @rebootEintrag in Ihrer crontab, um ein Skript mit dem Namen auszuführen /usr/local/bin/runonce.

Erstellen Sie eine Verzeichnisstruktur mit dem Namen /etc/local/runonce.d/ranusing mkdir -p.

Erstellen Sie das Skript /usr/local/bin/runoncewie folgt:

#!/bin/sh
for file in /etc/local/runonce.d/*
do
    if [ ! -f "$file" ]
    then
        continue
    fi
    "$file"
    mv "$file" "/etc/local/runonce.d/ran/$file.$(date +%Y%m%dT%H%M%S)"
    logger -t runonce -p local3.info "$file"
done

Legen Sie nun ein Skript, das Sie beim nächsten Neustart ausführen möchten (nur einmal) in das Verzeichnis /etc/local/runonce.dund chownund chmod +xes entsprechend. Sobald es ausgeführt wurde, wird es in das ranUnterverzeichnis verschoben und das Datum und die Uhrzeit werden an den Namen angehängt. Es wird auch einen Eintrag in Ihrer Datenbank geben syslog.

Bis auf weiteres angehalten.
quelle
2
Danke für deine Antwort. Diese Lösung ist großartig. Es löst mein Problem technisch, es scheint jedoch, dass eine Menge Infrastrukturvorbereitungen erforderlich sind, damit dies funktioniert. Es ist nicht tragbar. Ich denke, Ihre Lösung wäre idealerweise in eine Linux-Distribution eingebunden (ich bin mir nicht sicher, warum!). Ihre Antwort hat meine ultimative Lösung inspiriert, die ich auch als Antwort gepostet habe. Danke noch einmal!
Christopher Parker
Warum hast du dich für local3 entschieden, im Vergleich zu anderen Einrichtungen zwischen 0 und 7?
Christopher Parker
3
@ Christopher: Ein Würfelwurf ist immer die beste Methode. Im Ernst, zum Beispiel war es egal und das ist der Schlüssel, auf dem mein Finger gelandet ist. Außerdem besitze ich keinen achteckigen Würfel.
Bis auf weiteres angehalten.
@ Tennis: Verstanden, danke. Zufällig ist local3 die lokale Einrichtung, die in angezeigt wird man logger.
Christopher Parker
Enthält die $fileVariable den vollständigen Pfad oder nur den Dateinamen?
Andrew Savinykh
21

Ich schätze die Bemühungen, die Dennis Williamson unternommen hat, sehr . Ich wollte es als Antwort auf diese Frage akzeptieren, da es jedoch elegant und einfach ist:

  • Ich hatte letztendlich das Gefühl, dass es zu viele Schritte erfordert, um es einzurichten.
  • Es erfordert Root-Zugriff.

Ich denke, seine Lösung wäre großartig als Out-of-the-Box-Feature einer Linux-Distribution.

Davon abgesehen habe ich mein eigenes Skript geschrieben, um mehr oder weniger das Gleiche wie Dennis 'Lösung zu erreichen. Es sind keine zusätzlichen Installationsschritte erforderlich und es ist kein Root-Zugriff erforderlich.

#!/bin/bash

if [[ $# -eq 0 ]]; then
    echo "Schedules a command to be run after the next reboot."
    echo "Usage: $(basename $0) <command>"
    echo "       $(basename $0) -p <path> <command>"
    echo "       $(basename $0) -r <command>"
else
    REMOVE=0
    COMMAND=${!#}
    SCRIPTPATH=$PATH

    while getopts ":r:p:" optionName; do
        case "$optionName" in
            r) REMOVE=1; COMMAND=$OPTARG;;
            p) SCRIPTPATH=$OPTARG;;
        esac
    done

    SCRIPT="${HOME}/.$(basename $0)_$(echo $COMMAND | sed 's/[^a-zA-Z0-9_]/_/g')"

    if [[ ! -f $SCRIPT ]]; then
        echo "PATH=$SCRIPTPATH" >> $SCRIPT
        echo "cd $(pwd)"        >> $SCRIPT
        echo "logger -t $(basename $0) -p local3.info \"COMMAND=$COMMAND ; USER=\$(whoami) ($(logname)) ; PWD=$(pwd) ; PATH=\$PATH\"" >> $SCRIPT
        echo "$COMMAND | logger -t $(basename $0) -p local3.info" >> $SCRIPT
        echo "$0 -r \"$(echo $COMMAND | sed 's/\"/\\\"/g')\""     >> $SCRIPT
        chmod +x $SCRIPT
    fi

    CRONTAB="${HOME}/.$(basename $0)_temp_crontab_$RANDOM"
    ENTRY="@reboot $SCRIPT"

    echo "$(crontab -l 2>/dev/null)" | grep -v "$ENTRY" | grep -v "^# DO NOT EDIT THIS FILE - edit the master and reinstall.$" | grep -v "^# ([^ ]* installed on [^)]*)$" | grep -v "^# (Cron version [^$]*\$[^$]*\$)$" > $CRONTAB

    if [[ $REMOVE -eq 0 ]]; then
        echo "$ENTRY" >> $CRONTAB
    fi

    crontab $CRONTAB
    rm $CRONTAB

    if [[ $REMOVE -ne 0 ]]; then
        rm $SCRIPT
    fi
fi

Speichern Sie dieses Skript (zB: runonce), chmod +xund laufen:

$ runonce foo
$ runonce "echo \"I'm up. I swear I'll never email you again.\" | mail -s \"Server's Up\" $(whoami)"

Im Falle eines Tippfehlers können Sie einen Befehl mit dem Flag -r aus der runonce-Warteschlange entfernen:

$ runonce fop
$ runonce -r fop
$ runonce foo

Die Verwendung von sudo funktioniert so, wie Sie es erwarten würden. Nützlich, um einen Server nur einmal nach dem nächsten Neustart zu starten.

myuser@myhost:/home/myuser$ sudo runonce foo
myuser@myhost:/home/myuser$ sudo crontab -l
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/root/.runonce_temp_crontab_10478 installed on Wed Jun  9 16:56:00 2010)
# (Cron version V5.0 -- $Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $)
@reboot /root/.runonce_foo
myuser@myhost:/home/myuser$ sudo cat /root/.runonce_foo
PATH=/usr/sbin:/bin:/usr/bin:/sbin
cd /home/myuser
foo
/home/myuser/bin/runonce -r "foo"

Einige Notizen:

  • Dieses Skript repliziert die Umgebung (PFAD, Arbeitsverzeichnis, Benutzer), in der es aufgerufen wurde.
  • Es ist so konzipiert, dass die Ausführung eines Befehls grundsätzlich verschoben wird, da er "genau hier, genau jetzt" bis nach der nächsten Startsequenz ausgeführt wird.
Christopher Parker
quelle
Ihr Skript sieht sehr praktisch aus. Eine Sache zu beachten ist, dass es destruktiv Kommentare aus der Crontab entfernt.
Bis auf weiteres angehalten.
@ Tennis: Danke. Ich hatte ursprünglich keinen zusätzlichen grep-Anruf, aber alle Kommentare häuften sich an. Drei für jedes Mal, wenn ich das Skript ausführte. Ich denke, ich werde das Skript so ändern, dass nur immer Kommentarzeilen entfernt werden, die den drei automatisch generierten Kommentaren ähneln.
Christopher Parker
@ Tennis: Fertig. Die Muster könnten wahrscheinlich besser sein, aber es funktioniert bei mir.
Christopher Parker
@ Tennis: Eigentlich, basierend auf crontab.c, denke ich, dass meine Muster in Ordnung sind. (Suchen Sie nach "DIESE DATEI NICHT BEARBEITEN" unter opensource.apple.com/source/cron/cron-35/crontab/crontab.c. )
Christopher Parker,
5

Erstellen Sie zB /root/runonce.sh:

#!/bin/bash
#your command here
sed -i '/runonce.sh/d' /etc/rc.local

Hinzufügen zu /etc/rc.local:

/root/runonce.sh
Kaffeslangen
quelle
Das ist reines Genie. Vergiss nicht, chmod + x the /root/runonce.sh zu benutzen. Dies ist perfekt für ein passendes Upgrade auf Azure-Computern, die hängen bleiben, weil walinuxagent das dpkg blockiert
CarComp
Das ist zu abgedreht (obwohl es auch das ist, was ich mir zuerst ausgedacht habe).
iBug
2

Richten Sie das Skript in /etc/rc5.d mit einem S99 ein und lassen Sie es sich nach dem Ausführen selbst löschen.

mpez0
quelle
2

Sie können dies mit dem atBefehl tun , obwohl ich bemerke, dass Sie nicht (zumindest auf RHEL 5, auf dem ich getestet habe) at @rebootoder verwenden können at reboot, aber Sie können at now + 2 minutesund dann verwenden shutdown -r now.

Dies erfordert nicht, dass das System länger als 2 Minuten ausgeführt wird.

Es kann nützlich sein, wenn Sie 0 einrichten möchten, obwohl ich mir eher wünsche, dass der Befehl 'runonce' ein Standard-Kit ist.

Cameron Kerr
quelle
Beachten Sie, dass atdIhr Befehl möglicherweise während des Herunterfahrens ausgeführt wird , wenn Ihr System länger als 2 Minuten bis zum Stopp benötigt. Dies kann vermieden werden, atdindem Sie den Befehl stoppen, bevor Sie ihn ablegen atd nowund anschließend neu starten.
mleu
2

Ich halte diese Antwort für die eleganteste:

Skript /etc/init.d/scripteinfügen und mit letzter Zeile selbst löschen:rm $0

Wenn das Skript nicht zu 100% ausfallsicher ist, sollten Sie wahrscheinlich Ausnahmen behandeln, um eine schwerwiegende Fehlerschleife zu vermeiden.

Geotheorie
quelle
2

Früher chkconfighabe ich auf meinem System ein Skript automatisch einmal nach dem Start ausgeführt und nie wieder. Wenn Ihr System ckconfig (Fedora, RedHat, CentOs usw.) verwendet, funktioniert dies.

Zuerst das Drehbuch:

#!/bin/bash
# chkconfig: 345 99 10
# description: This script is designed to run once and then never again.
#


##
# Beginning of your custom one-time commands
#

plymouth-set-default-theme charge -R
dracut -f

#
# End of your custom one-time commands
##


##
# This script will run once
# If you would like to run it again.  run 'chkconfig run-once on' then reboot.
#
chkconfig run-once off
chkconfig --del run-once
  1. Nennen Sie das Skript run-once
  2. Legen Sie das Skript in /etc/init.d/
  3. Aktivieren Sie das Skript chkconfig run-once on
  4. neustarten

Wenn das System hochfährt, wird Ihr Skript einmal und nie wieder ausgeführt.

Das heißt, nie wieder, es sei denn, Sie möchten es. Sie können das Skript jederzeit mit dem chkconfig run-once onBefehl wieder aktivieren .

Ich mag diese Lösung, weil sie nur eine einzige Datei auf dem System ablegt und weil der Befehl run-once bei Bedarf erneut ausgegeben werden kann.

Spitzmaus
quelle
-1

In RedHat- und Debian-Systemen können Sie dies über /etc/rc.local tun, es ist eine Art autoexec.bat.

Natxo Asenjo
quelle
7
Das wird bei jedem Boot ausgeführt, nicht nur beim nächsten.
Bis auf weiteres angehalten.
1
Ich habe die Frage falsch verstanden. Vielen Dank für die Korrektur
Natxo Asenjo