Frage Linux: Zeitplan-Befehl, der nach dem Neustart einmal ausgeführt wird (RunOnce-Äquivalent)


Ich möchte einen Befehl planen, der nach dem Neustart auf einer Linux-Box ausgeführt werden soll. Ich weiß wie das geht, so der Befehl läuft nach jedem Neustart regelmäßig mit einer @reboot crontab-Eintrag, aber ich möchte nur, dass der Befehl 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 Renn einmal 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?


21
2018-06-04 19:46


Ursprung


RunOnce ist ein Artefakt von Windows aufgrund von Problemen beim Beenden der Konfiguration vor einem Neustart. 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:


Erstelle ein @reboot Eintrag in Ihrer Crontab, um ein Skript namens /usr/local/bin/runonce.

Erstellen Sie eine Verzeichnisstruktur namens /etc/local/runonce.d/ran verwenden mkdir -p.

Erstellen Sie das Skript /usr/local/bin/runonce wie 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

Platzieren Sie jetzt alle Skripte, die beim nächsten Neustart (nur einmal) ausgeführt werden sollen, im Verzeichnis /etc/local/runonce.d und chown und chmod +x es angemessen. Sobald es ausgeführt wurde, werden Sie feststellen, dass es in die ran Unterverzeichnis und das Datum und die Uhrzeit an seinen Namen angehängt. Es wird auch einen Eintrag in Ihrem geben syslog.


34
2018-06-04 20:41



Danke für deine Antwort. Diese Lösung ist großartig. Es löst mein Problem technisch, aber es scheint, als ob es eine Menge an Vorbereitung der Infrastruktur gibt, die benötigt wird, um diese Arbeit zu machen. Es ist nicht tragbar. Ich denke, Ihre Lösung würde idealerweise in eine Linux-Distribution integriert werden (ich bin mir nicht sicher, warum das nicht so ist!). Deine Antwort hat meine ultimative Lösung inspiriert, die ich auch als Antwort gepostet habe. Danke noch einmal! - Christopher Parker
Was hat Sie dazu gebracht, local3 im Vergleich zu anderen Einrichtungen zwischen 0 und 7 zu wählen? - Christopher Parker
@Christopher: Ein Würfelwurf ist immer die beste Methode. Aber im Ernst, für ein Beispiel war es egal und das ist der Schlüssel, auf den mein Finger landete. Außerdem besitze ich keinen achtseitigen Würfel. - Dennis Williamson
@Dennis: Verstanden, danke. Zufälligerweise ist local3 die lokale Einrichtung, die in erscheint man logger. - Christopher Parker
Macht der $file Variable enthält den vollständigen Pfad oder nur den Dateinamen? - Andrew Savinykh


Ich schätze den Aufwand sehr Dennis Williamson's Antwort. Ich wollte es als Antwort auf diese Frage akzeptieren, da es elegant und einfach ist:

  • Ich hatte schließlich das Gefühl, dass zu viele Schritte erforderlich waren.
  • Es erfordert Root-Zugriff.

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

Davon abgesehen schrieb ich mein eigenes Skript, um mehr oder weniger dasselbe wie Dennis 'Lösung zu erreichen. Es erfordert keine zusätzlichen Konfigurationsschritte und erfordert keinen Root-Zugriff.

#!/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 (z. B .: runonce), chmod +x, und Renn:

$ 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-Queue entfernen:

$ runonce fop
$ runonce -r fop
$ runonce foo

Sudo funktioniert so, wie Sie es erwarten würden. Nützlich zum Starten eines Servers nur einmal nach dem nächsten Neustart.

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 (PATH, Arbeitsverzeichnis, Benutzer), in der es aufgerufen wurde.
  • Es wurde entwickelt, um die Ausführung eines Befehls im Grunde so zu verzögern, als würde er "genau hier, gerade jetzt" bis nach der nächsten Startsequenz ausgeführt werden.

20
2018-06-11 01:35



Dein Skript sieht wirklich praktisch aus. Eine Sache, die zu beachten ist, ist, dass es Kommentare aus der Crontab löscht. - Dennis Williamson
@Dennis: Danke. Ich hatte ursprünglich nicht diesen zusätzlichen Grep-Call, aber alle Kommentare häuften sich; drei für jedes Mal, wenn ich das Skript lief. Ich denke, ich werde das Skript so ändern, dass nur Kommentarzeilen entfernt werden, die wie diese drei automatisch generierten Kommentare aussehen. - Christopher Parker
@Dennis: Fertig. Die Muster könnten wahrscheinlich besser sein, aber es funktioniert für mich. - Christopher Parker
@Dennis: Eigentlich, basierend auf crontab.c, denke ich, dass meine Muster gut sind. (Suchen Sie nach "BEARBEITEN SIE DIESE DATEI NICHT" bei opensource.apple.com/source/cron/cron-35/contab/contab.c.) - Christopher Parker


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


2
2018-05-15 12:02





Du kannst das mit dem tun at Befehl, obwohl ich merke, dass Sie nicht (zumindest auf RHEL 5, welches ich getestet habe) verwenden können at @reboot oder at reboot, aber du kannst es benutzen at now + 2 minutes und dann shutdown -r now.

Dies erfordert nicht, dass das System länger als 2 Minuten zum Ausführen benötigt.

Es kann nützlich sein, wo Sie wollen, 0-Setup, obwohl ich lieber möchte, dass der 'Runonce' Befehl Standard-Kit war.


2
2017-09-04 14:19



Beachten Sie, dass wenn Ihr System länger als 2 Minuten dauert, bis atd gestoppt ist, kann Ihr Befehl während des Herunterfahrens ausgeführt werden. Dies könnte durch Stoppen vermieden werden atd bevor Sie den Befehl mit übergeben atd now und dann neu starten. - mleu


Meiner Ansicht nach diese Antwort ist das eleganteste:

Legen Sie das Skript ein /etc/init.d/script und self-delete mit der letzten Zeile: rm $0

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


2
2018-04-29 19:26





ich benutzte chkconfig um mein System automatisch nach dem Booten und nie wieder ein Skript ausführen zu lassen. Wenn Ihr System ckconfig (Fedora, RedHat, CentOs, etc.) verwendet, wird dies funktionieren.

Zuerst das Skript:

#!/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. Benennen Sie das Skript run-once
  2. Legen Sie das Skript ein /etc/init.d/
  3. Aktivieren Sie das Skript chkconfig run-once on
  4. neu starten

Wenn das System bootet, wird das Skript einmal und nie wieder ausgeführt.

Das heißt, nie wieder, außer du willst es. Sie können das Skript immer wieder mit dem aktivieren chkconfig run-once on Befehl.

Ich mag diese Lösung, weil sie nur eine einzige Datei auf dem System ablegt und weil der Befehl "einmal ausführen" bei Bedarf neu ausgegeben werden kann.


1
2017-11-24 09:06





Erstellen Sie z.B. /root/runonce.sh:

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

Hinzufügen /etc/rc.local:

/root/runonce.sh

1
2018-06-05 20:44



Das ist pures Genie. Vergiss nicht chmod + x /root/runonce.sh. Dies ist perfekt für das apt-get-Upgrade auf azurblauen Rechnern, die hängen, weil walinuxagent das dpkg blockiert - CarComp