Linux: команда Schedule для запуску один раз після перезавантаження (еквівалент RunOnce)


26

Я хотів би запланувати команду для запуску після перезавантаження на вікні Linux. Я знаю, як це зробити, щоб команда послідовно виконувалася після кожної перезавантаження із записом @rebootcrontab, проте я хочу лише, щоб команда виконувалася лише один раз. Після запуску його слід видалити з черги команд для запуску. Я по суті шукаю Linux еквівалент RunOnce у світі Windows.

У випадку, якщо це має значення:

$ 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

Чи є простий, чіткий спосіб зробити це?


1
RunOnce - артефакт Windows, що виникає внаслідок проблем із завершенням налаштування перед перезавантаженням. Чи є причина, що ви не можете запустити свій скрипт перед перезавантаженням? Вищеописане рішення представляється розумним клоном RunOnce.
BillThor

Відповіді:


38

Створіть @rebootзапис у своєму Crontab для запуску сценарію під назвою /usr/local/bin/runonce.

Створіть структуру каталогу, яку називають /etc/local/runonce.d/ranза допомогою mkdir -p.

Створіть сценарій /usr/local/bin/runonceнаступним чином:

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

Тепер помістіть будь-який скрипт , який ви хочете запустити після перезавантаження сторінки (тільки один раз) в каталозі /etc/local/runonce.dі chownта chmod +xйого відповідним чином . Після запуску ви побачите, що він переміщений до ranпідкаталогу, а дата та час додаються до його імені. Також буде запис у вашому syslog.


2
Дякую за вашу відповідь. Це рішення чудове. Це технічно вирішує мою проблему, однак, здається, для підготовки цієї роботи потрібно багато підготовки інфраструктури. Це не портативно. Я думаю, що ваше рішення в ідеалі було б викладено в дистрибутив Linux (я не впевнений, чому це не так!). Ваша відповідь надихнула мого остаточного рішення, яке я також опублікував як відповідь. Знову дякую!
Крістофер Паркер

Що змусило вас обрати local3 порівняно з будь-яким з інших об'єктів між 0 і 7?
Крістофер Паркер

3
@Christopher: рулет з кістки - це завжди найкращий метод. Однак, якщо це серйозно, для прикладу це не мало значення, і це ключ, на який приземлився мій палець. Крім того, я не маю жодної восьмигранної смерті.
Призупинено до подальшого повідомлення.

@Dennis: Зрозумів, спасибі За збігом обставин, local3 - це локальний об'єкт, який з'являється в man logger.
Крістофер Паркер

Чи $fileмістить змінна повний шлях або лише ім'я файлу?
Андрій Савіних

21

Я дуже ціную зусилля, докладені до відповіді Денніса Вільямсона . Я хотів прийняти це як відповідь на це питання, оскільки воно елегантне і просте, проте:

  • Я врешті-решт відчув, що для створення цього потрібно занадто багато кроків.
  • Він вимагає кореневого доступу.

Я думаю, що його рішення було б чудовим як функція нестандартної дистрибуції Linux.

Попри це, я написав власний сценарій, щоб виконати більш-менш те саме, що і рішення Денніса. Він не потребує додаткових кроків налаштування, а також не потребує кореневого доступу.

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

Збережіть цей скрипт (наприклад: runonce), chmod +xі запуск:

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

У разі помилки ви можете видалити команду з черги runonce із прапором -r:

$ runonce fop
$ runonce -r fop
$ runonce foo

Використання судо працює так, як ви б очікували, що воно працює. Корисно для запуску сервера лише один раз після чергової перезавантаження.

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"

Деякі примітки:

  • Цей скрипт копіює середовище (PATH, робочий каталог, користувач), в яке було викликано.
  • Він в основному призначений для відстрочки виконання команди, оскільки вона буде виконуватися "прямо тут, прямо зараз" до наступної послідовності завантаження.

Ваш сценарій виглядає дуже зручно. Варто зазначити, що вона руйнівно знімає коментарі з кронтобу.
Призупинено до подальшого повідомлення.

@Dennis: Дякую Спочатку у мене не було такого зайвого дзвінка, але всі коментарі накопичувались; три за кожен раз, коли я запускав сценарій. Я думаю, що я зміню сценарій, щоб завжди завжди видаляти рядки коментарів, схожі на ці три автоматично створені коментарі.
Крістофер Паркер

@Dennis: Готово. Можливо, шаблони могли бути кращими, але це працює для мене.
Крістофер Паркер

@Dennis: Насправді, базуючись на crontab.c, я думаю, що мої шаблони просто чудові. (Шукайте "НЕ редагувати цей файл" за адресою opensource.apple.com/source/cron/cron-35/crontab/crontab.c. )
Крістофер Паркер

5

Створити, наприклад /root/runonce.sh:

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

Додати до /etc/rc.local:

/root/runonce.sh

Це чистий геній. Не забудьте chmod + x the /root/runonce.sh. Це ідеально підходить для вдосконалення apt-get на лазурних машинах, які висять, оскільки walinuxagent блокує dpkg
CarComp

Це занадто гакітно (хоча це теж те, що я придумав спочатку).
iBug

2

Встановіть скрипт у /etc/rc5.d за допомогою S99 та видаліть його після запуску.


2

Ви можете зробити це за допомогою atкоманди, хоча я помітив , що ви не можете (принаймні , на RHEL 5, який я тестував на) використання at @rebootабо at reboot, але ви можете використовувати at now + 2 minutesі потім shutdown -r now.

Для цього не потрібно, щоб у вас система зайняла більше 2 хвилин.

Це може бути корисно там, де ви хочете налаштувати 0, хоча я дуже хочу, щоб команда 'runonce' була стандартною комплектацією.


Будьте уважні, якщо ваша система займе більше 2 хвилин, поки її atdне зупиняють, ваша команда може працювати під час відключення. Цього можна уникнути, зупинившись atdперед тим, як скинути команду, atd nowа потім перезавантажити.
mleu

2

Я вважаю, що ця відповідь є найелегантнішою:

Помістіть скрипт у /etc/init.d/scriptта самостійно видаліть останнім рядком:rm $0

Якщо сценарій не є на 100% відмовою, можливо, розумно обробляти винятки, щоб уникнути фатальної помилки.


2

У мене раніше chkconfigсистема автоматично запускала сценарій один раз після завантаження і ніколи більше. Якщо ваша система використовує ckconfig (Fedora, RedHat, CentOs тощо), це працюватиме.

Спочатку сценарій:

#!/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. Назвіть сценарій run-once
  2. Помістіть сценарій у /etc/init.d/
  3. Увімкніть сценарій chkconfig run-once on
  4. перезавантажити

Коли система завантажиться, ваш сценарій запуститься один раз і ніколи більше.

Тобто, ніколи більше, якщо ви цього не хочете. Завжди можна знову включити скрипт за допомогою chkconfig run-once onкоманди.

Мені подобається це рішення, оскільки воно ставить один і єдиний файл у системі і тому, що команда run-Once може бути перездана за потреби.


-1

в системах redhat і debian ви можете це зробити з /etc/rc.local, це свого роду autoexec.bat.


7
Це буде виконуватися при кожному завантаженні, а не тільки наступному.
Призупинено до подальшого повідомлення.

1
моє погано, я неправильно запитав питання. Дякую за виправлення
natxo asenjo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.