Linux: comando Schedule da eseguire una volta dopo il riavvio (equivalente RunOnce)


26

Vorrei programmare un comando da eseguire dopo il riavvio su un box Linux. So come fare in modo che il comando venga eseguito regolarmente dopo ogni riavvio con una @rebootvoce crontab, tuttavia voglio solo che il comando venga eseguito una volta. Dopo l'esecuzione, dovrebbe essere rimosso dalla coda dei comandi per l'esecuzione. Sto essenzialmente cercando un equivalente di Linux a RunOnce nel mondo di Windows.

Nel caso in cui sia importante:

$ 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

Esiste un modo semplice e programmabile per farlo?


1
RunOnce è un artefatto di Windows derivante da problemi nel completamento della configurazione prima di un riavvio. C'è qualche motivo per cui non è possibile eseguire lo script prima del riavvio? La soluzione sopra sembra essere un clone ragionevole di RunOnce.
BillThor,

Risposte:


38

Crea una @rebootvoce nel tuo crontab per eseguire uno script chiamato /usr/local/bin/runonce.

Creare una struttura di directory chiamata /etc/local/runonce.d/ranusando mkdir -p.

Crea lo script /usr/local/bin/runoncecome segue:

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

Ora inserire qualsiasi script che si desidera eseguire al successivo riavvio (una sola volta) nella directory /etc/local/runonce.ded chowne chmod +xin modo appropriato. Una volta eseguito, lo troverai spostato nella ransottodirectory e la data e l'ora aggiunte al suo nome. Ci sarà anche una voce nel tuo syslog.


2
Grazie per la tua risposta. Questa soluzione è fantastica. Risolve tecnicamente il mio problema, tuttavia sembra che ci sia molta preparazione dell'infrastruttura necessaria per far funzionare questo. Non è portatile. Penso che la tua soluzione sarebbe idealmente inserita in una distribuzione Linux (non sono sicuro del perché!). La tua risposta ha ispirato la mia soluzione definitiva, che ho anche pubblicato come risposta. Grazie ancora!
Christopher Parker,

Cosa ti ha fatto scegliere local3, rispetto a qualsiasi altra struttura tra 0 e 7?
Christopher Parker,

3
@Christopher: un tiro di dadi è sempre il metodo migliore. Scherzi a parte, ad esempio, non importava e questa è la chiave su cui è atterrato il mio dito. Inoltre, non possiedo alcun dado a otto facce.
In pausa fino a nuovo avviso.

@Dennis: capito, grazie. Per coincidenza, local3 è la struttura locale che appare in man logger.
Christopher Parker,

La $filevariabile contiene il percorso completo o solo il nome del file?
Andrew Savinykh,

21

Apprezzo molto lo sforzo messo nella risposta di Dennis Williamson . Volevo accettarlo come risposta a questa domanda, in quanto è elegante e semplice, tuttavia:

  • Alla fine ho sentito che erano necessari troppi passaggi per l'installazione.
  • Richiede l'accesso come root.

Penso che la sua soluzione sarebbe ottima come funzionalità out-of-the-box di una distribuzione Linux.

Detto questo, ho scritto la mia sceneggiatura per realizzare più o meno la stessa cosa della soluzione di Dennis. Non richiede ulteriori passaggi di installazione e non richiede l'accesso come root.

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

Salva questo script (ad esempio: runonce), chmod +xed eseguire:

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

In caso di errore di battitura, è possibile rimuovere un comando dalla coda runonce con il flag -r:

$ runonce fop
$ runonce -r fop
$ runonce foo

L'uso di sudo funziona come ti aspetteresti che funzioni. Utile per avviare un server una sola volta dopo il riavvio successivo.

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"

Alcune note:

  • Questo script replica l'ambiente (PATH, directory di lavoro, utente) in cui è stato invocato.
  • È progettato per rimandare sostanzialmente l'esecuzione di un comando poiché verrebbe eseguito "proprio qui, proprio ora" fino alla successiva sequenza di avvio.

Il tuo script sembra davvero utile. Una cosa da notare è che elimina in modo distruttivo i commenti dal crontab.
In pausa fino a nuovo avviso.

@Dennis: grazie. Inizialmente non avevo quella chiamata grep extra lì dentro, ma tutti i commenti si stavano accumulando; tre per ogni volta che ho eseguito la sceneggiatura. Penso che cambierò lo script per rimuovere sempre solo le righe di commento che sembrano quei tre commenti generati automaticamente.
Christopher Parker,

@Dennis: Fatto. Gli schemi potrebbero probabilmente essere migliori, ma funziona per me.
Christopher Parker,

@Dennis: In realtà, basato su crontab.c, penso che i miei schemi vadano bene. (Cerca "NON MODIFICARE QUESTO FILE" su opensource.apple.com/source/cron/cron-35/crontab/crontab.c. )
Christopher Parker,

5

Crea ad es . /root/runonce.sh:

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

Aggiungi a /etc/rc.local:

/root/runonce.sh

Questo è puro genio. Non dimenticare di chmod + x the /root/runonce.sh. Questo è perfetto per apt-get upgrade su macchine azzurre che si bloccano perché walinuxagent blocca il dpkg
CarComp

Questo è troppo confuso (anche se all'inizio è quello che mi è venuto in mente).
iBug

2

Imposta lo script in /etc/rc5.d con un S99 e fallo cancellare dopo l'esecuzione.


2

Puoi farlo con il atcomando, anche se noto che non puoi (almeno su RHEL 5, su cui ho testato) usare at @rebooto at reboot, ma puoi usare at now + 2 minutese poi shutdown -r now.

Ciò non richiede che il sistema impieghi più di 2 minuti per l'esecuzione.

Può essere utile dove vuoi 0 set-up, anche se preferirei che il comando 'runonce' fosse un kit standard.


Fare attenzione che se il sistema impiega più di 2 minuti fino atdall'arresto, il comando potrebbe essere eseguito durante l'arresto. Ciò potrebbe essere evitato arrestandosi atdprima di ripulire il comando con atd nowe quindi riavviare.
mleu,

2

Penso che questa risposta sia la più elegante:

Inserisci lo script /etc/init.d/scripted eliminalo automaticamente con l'ultima riga:rm $0

A meno che lo script non sia a prova di errore al 100%, probabilmente è consigliabile gestire le eccezioni per evitare un errore irreversibile.


2

In passato il chkconfigmio sistema eseguiva automaticamente uno script dopo l'avvio e mai più. Se il tuo sistema utilizza ckconfig (Fedora, RedHat, CentOs, ecc.) Funzionerà.

Innanzitutto la sceneggiatura:

#!/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. Dai un nome alla sceneggiatura run-once
  2. Inserisci lo script in /etc/init.d/
  3. Abilita lo script chkconfig run-once on
  4. riavvio

Quando il sistema si avvia, lo script verrà eseguito una volta e mai più.

Cioè, mai più a meno che tu non lo voglia. Puoi sempre riattivare lo script con il chkconfig run-once oncomando.

Mi piace questa soluzione perché mette un solo file sul sistema e perché il comando run-once può essere emesso di nuovo se necessario.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.