Linux: Zaplanuj uruchomienie polecenia po ponownym uruchomieniu (odpowiednik RunOnce)

26

Chciałbym zaplanować uruchamianie polecenia po ponownym uruchomieniu komputera z systemem Linux. Wiem, jak to zrobić, więc polecenie konsekwentnie uruchamia się po każdym ponownym uruchomieniu z @rebootwpisem crontab, jednak chcę, aby polecenie było uruchamiane tylko raz. Po uruchomieniu należy go usunąć z kolejki poleceń do uruchomienia. Zasadniczo szukam systemu Linux odpowiadającego RunOnce w świecie Windows.

W przypadku, gdy ma to znaczenie:

$ 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

Czy istnieje prosty sposób na wykonanie skryptów?

Christopher Parker
źródło
1
RunOnce to artefakt systemu Windows wynikający z problemów z ukończeniem konfiguracji przed ponownym uruchomieniem. Czy jest jakiś powód, dla którego nie można uruchomić skryptu przed ponownym uruchomieniem? Powyższe rozwiązanie wydaje się być rozsądnym klonem RunOnce.
BillThor

Odpowiedzi:

38

Utwórz @rebootwpis w swoim crontab, aby uruchomić skrypt o nazwie /usr/local/bin/runonce.

Utwórz strukturę katalogów o nazwie /etc/local/runonce.d/ranusing mkdir -p.

Utwórz skrypt /usr/local/bin/runoncew następujący sposób:

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

Teraz umieścić dowolny skrypt, który chcesz uruchomić przy następnym restarcie (tylko jeden raz) w katalogu /etc/local/runonce.di chowni chmod +xgo odpowiednio. Po uruchomieniu zostanie przeniesiony do ranpodkatalogu, a data i godzina dołączone do jego nazwy. W twoim będzie również wpis syslog.

Wstrzymano do odwołania.
źródło
2
Dzięki za odpowiedź. To rozwiązanie jest świetne. Technicznie rozwiązuje to mój problem, ale wydaje się, że do wykonania tej pracy konieczne jest przygotowanie infrastruktury. To nie jest przenośne. Myślę, że Twoje rozwiązanie idealnie byłoby wkomponowane w dystrybucję Linuksa (nie jestem pewien, dlaczego tak nie jest!). Twoja odpowiedź zainspirowała moje najlepsze rozwiązanie, które również zamieściłem jako odpowiedź. Dzięki jeszcze raz!
Christopher Parker,
Co sprawiło, że wybrałeś local3 w porównaniu z innymi urządzeniami z przedziału od 0 do 7?
Christopher Parker
3
@Christopher: Rzut kostką jest zawsze najlepszą metodą. Poważnie, na przykład, nie miało to znaczenia i to jest klucz, na który wylądował mój palec. Poza tym nie posiadam żadnej ośmiościennej kości.
Wstrzymano do odwołania.
@Dennis: Rozumiem, dzięki. Przypadkowo, local3 to lokalny obiekt, który pojawia się w man logger.
Christopher Parker,
Czy $filezmienna zawiera pełną ścieżkę czy tylko nazwę pliku?
Andrew Savinykh
21

Naprawdę doceniam wysiłek włożony w odpowiedź Dennisa Williamsona . Chciałem zaakceptować to jako odpowiedź na to pytanie, ponieważ jest ono eleganckie i proste:

  • W końcu czułem, że wymagało to zbyt wielu kroków.
  • Wymaga dostępu do roota.

Myślę, że jego rozwiązanie byłoby świetne jako gotowa funkcja dystrybucji Linuksa.

To powiedziawszy, napisałem własny skrypt, aby osiągnąć mniej więcej to samo, co rozwiązanie Dennisa. Nie wymaga żadnych dodatkowych kroków konfiguracji i nie wymaga dostępu do konta 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

Zapisz ten skrypt (np runonce), chmod +xi wykonaj:

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

W przypadku literówki możesz usunąć polecenie z kolejki uruchamiania za pomocą opcji -r:

$ runonce fop
$ runonce -r fop
$ runonce foo

Korzystanie z sudo działa tak, jak byś tego oczekiwał. Przydatne do uruchomienia serwera tylko raz po następnym restarcie.

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"

Niektóre uwagi:

  • Ten skrypt replikuje środowisko (PATH, katalog roboczy, użytkownik), w którym został wywołany.
  • Został zaprojektowany tak, aby po prostu odroczyć wykonanie polecenia, ponieważ zostałoby ono wykonane „tu i teraz” aż do następnej sekwencji rozruchowej.
Christopher Parker
źródło
Twój skrypt wygląda na naprawdę przydatny. Należy zauważyć, że destrukcyjnie usuwa komentarze z pliku crontab.
Wstrzymano do odwołania.
@Dennis: Dzięki. Początkowo nie miałem tam dodatkowego wywołania grep, ale wszystkie komentarze były coraz większe; trzy za każdym razem, gdy uruchamiam skrypt. Myślę, że zmienię skrypt, aby zawsze usuwał wiersze komentarzy wyglądające jak te trzy automatycznie wygenerowane komentarze.
Christopher Parker
@Dennis: Gotowe. Wzory mogą być prawdopodobnie lepsze, ale to działa dla mnie.
Christopher Parker,
@Dennis: Właściwie, oparte na crontab.c, myślę, że moje wzory są w porządku. (Wyszukaj „NIE EDYTUJ TEGO PLIKU” na opensource.apple.com/source/cron/cron-35/crontab/crontab.c. )
Christopher Parker
5

Utwórz np . /root/runonce.sh:

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

Dodaj do /etc/rc.local:

/root/runonce.sh
kaffeslangen
źródło
To jest czysty geniusz. Nie zapomnij o chmod + x /root/runonce.sh. Jest to idealne rozwiązanie do aktualizacji apt-get na komputerach lazurowych, które się zawieszają, ponieważ walinuxagent blokuje dpkg
CarComp
To jest zbyt zuchwałe (choć na początku to też wymyśliłem).
iBug
2

Skonfiguruj skrypt w /etc/rc5.d za pomocą S99 i niech sam się usunie po uruchomieniu.

mpez0
źródło
2

Możesz to zrobić za pomocą atpolecenia, chociaż zauważam, że nie możesz (przynajmniej na RHEL 5, na którym testowałem) używać at @rebootlub at reboot, ale możesz użyć at now + 2 minutesi wtedy shutdown -r now.

Nie wymaga to, aby system działał dłużej niż 2 minuty.

Może być przydatny tam, gdzie chcesz ustawić 0, chociaż wolę raczej, aby polecenie „runonce” było standardowym zestawem.

Cameron Kerr
źródło
Uważaj, jeśli jeśli system atdzostanie zatrzymany dłużej niż 2 minuty, polecenie może zostać uruchomione podczas zamykania systemu. Można tego uniknąć, zatrzymując się atdprzed przekazaniem polecenia za pomocą, atd nowa następnie ponownie uruchamiając.
mleu
2

Myślę, że ta odpowiedź jest najbardziej elegancka:

Umieść skrypt /etc/init.d/scripti usuń go sam z ostatnim wierszem:rm $0

Chyba że skrypt jest w 100% odporny na awarie, prawdopodobnie rozsądnie jest obsługiwać wyjątki, aby uniknąć krytycznej pętli błędów.

geotheory
źródło
2

Kiedyś chkconfigmój system automatycznie uruchamiał skrypt po rozruchu i nigdy więcej. Jeśli twój system używa ckconfig (Fedora, RedHat, CentOs itp.), To zadziała.

Najpierw skrypt:

#!/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. Nazwij skrypt run-once
  2. Umieść skrypt w /etc/init.d/
  3. Włącz skrypt chkconfig run-once on
  4. restart

Po uruchomieniu systemu skrypt będzie działał raz i nigdy więcej.

To znaczy, nigdy więcej, chyba że tego chcesz. Zawsze możesz ponownie włączyć skrypt za pomocą chkconfig run-once onpolecenia.

Podoba mi się to rozwiązanie, ponieważ umieszcza w systemie jeden i tylko jeden plik oraz ponieważ w razie potrzeby można ponownie wydać polecenie „uruchom raz”.

ryjówka
źródło
-1

w systemach redhat i debian możesz to zrobić z /etc/rc.local, jest to rodzaj autoexec.bat.

natxo asenjo
źródło
7
To będzie wykonywane przy każdym rozruchu, a nie tylko następnym.
Wstrzymano do odwołania.
1
mój zły, źle odczytałem pytanie. Dzięki za korektę
natxo asenjo