Linux: Jadwalkan perintah untuk menjalankan satu kali setelah reboot (setara RunOnce)

26

Saya ingin menjadwalkan perintah untuk dijalankan setelah reboot pada kotak Linux. Saya tahu bagaimana melakukan ini sehingga perintah secara konsisten berjalan setelah setiap reboot dengan @rebootentri crontab, namun saya hanya ingin perintah dijalankan sekali. Setelah berjalan, harus dihapus dari antrian perintah untuk dijalankan. Saya pada dasarnya mencari Linux yang setara dengan RunOnce di dunia Windows.

Dalam hal itu penting:

$ 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

Apakah ada cara mudah dan skrip untuk melakukan ini?

Christopher Parker
sumber
1
RunOnce adalah artefak Windows yang dihasilkan dari masalah menyelesaikan konfigurasi sebelum reboot. Apakah ada alasan Anda tidak dapat menjalankan skrip Anda sebelum reboot? Solusi di atas tampaknya merupakan klon yang masuk akal dari RunOnce.
BillThor

Jawaban:

38

Buat @rebootentri di crontab Anda untuk menjalankan skrip bernama /usr/local/bin/runonce.

Buat struktur direktori yang disebut /etc/local/runonce.d/ranusing mkdir -p.

Buat skrip /usr/local/bin/runoncesebagai berikut:

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

Sekarang menempatkan script apapun yang Anda ingin dijalankan pada reboot berikutnya (sekali saja) di direktori /etc/local/runonce.ddan chowndan chmod +xitu tepat. Setelah dijalankan, Anda akan menemukannya dipindahkan ke ransubdirektori dan tanggal dan waktu ditambahkan ke namanya. Juga akan ada entri di blog Anda syslog.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
2
Terima kasih atas jawaban anda. Solusi ini sangat bagus. Secara teknis menyelesaikan masalah saya, namun sepertinya ada banyak persiapan infrastruktur yang diperlukan untuk membuat pekerjaan ini. Ini tidak portabel. Saya pikir solusi Anda idealnya akan dimasukkan ke dalam distribusi Linux (saya tidak yakin mengapa tidak!). Jawaban Anda menginspirasi solusi utama saya, yang juga saya poskan sebagai jawaban. Terima kasih lagi!
Christopher Parker
Apa yang membuat Anda memilih local3, dibandingkan dengan fasilitas lain antara 0 dan 7?
Christopher Parker
3
@Christopher: Gulungan dadu selalu merupakan metode terbaik. Serius, sebagai contoh, itu tidak masalah dan itu adalah kunci jariku. Selain itu, saya tidak memiliki delapan sisi mati.
Dijeda sampai pemberitahuan lebih lanjut.
@ Dennis: Mengerti, terima kasih. Secara kebetulan, local3 adalah fasilitas lokal yang muncul di man logger.
Christopher Parker
Apakah $filevariabel berisi path lengkap atau hanya nama file?
Andrew Savinykh
21

Saya sangat menghargai upaya yang dimasukkan ke dalam jawaban Dennis Williamson . Saya ingin menerimanya sebagai jawaban untuk pertanyaan ini, karena elegan dan sederhana, namun:

  • Saya akhirnya merasa bahwa itu membutuhkan terlalu banyak langkah untuk diatur.
  • Itu membutuhkan akses root.

Saya pikir solusinya akan bagus sebagai fitur out-of-the-box dari distribusi Linux.

Yang sedang berkata, saya menulis skrip saya sendiri untuk menyelesaikan kurang lebih hal yang sama dengan solusi Dennis. Itu tidak memerlukan langkah-langkah pengaturan tambahan dan tidak memerlukan akses 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

Simpan script ini (misalnya: runonce), chmod +x, dan menjalankan:

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

Jika terjadi kesalahan ketik, Anda dapat menghapus perintah dari antrian runonce dengan flag -r:

$ runonce fop
$ runonce -r fop
$ runonce foo

Menggunakan sudo berfungsi seperti yang Anda harapkan berfungsi. Berguna untuk memulai server hanya sekali setelah reboot berikutnya.

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"

Beberapa catatan:

  • Skrip ini mereplikasi lingkungan (PATH, direktori kerja, pengguna) yang dipanggil.
  • Ini pada dasarnya dirancang untuk menunda eksekusi perintah karena akan dieksekusi "di sini, sekarang" sampai setelah urutan boot berikutnya.
Christopher Parker
sumber
Script Anda terlihat sangat berguna. Satu hal yang perlu diperhatikan adalah bahwa itu merusak komentar dari crontab.
Dijeda sampai pemberitahuan lebih lanjut.
@ Dennis: Terima kasih. Awalnya saya tidak memiliki panggilan grep tambahan di sana, tetapi semua komentar menumpuk; tiga untuk setiap kali saya menjalankan skrip. Saya pikir saya akan mengubah skrip untuk selalu menghapus baris komentar yang terlihat seperti ketiga komentar yang dibuat secara otomatis.
Christopher Parker
@ Dennis: Selesai. Polanya mungkin bisa lebih baik, tetapi itu bekerja untuk saya.
Christopher Parker
@ Dennis: Sebenarnya, berdasarkan crontab.c, saya pikir polanya baik-baik saja. (Cari "JANGAN EDIT FILE INI" di opensource.apple.com/source/cron/cron-35/crontab/crontab.c. )
Christopher Parker
5

Buat misalnya /root/runonce.sh:

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

Tambahkan ke /etc/rc.local:

/root/runonce.sh
kaffeslangen
sumber
Ini jenius murni. Jangan lupa chmod + x the /root/runonce.sh. Ini sempurna untuk peningkatan apt-get pada mesin biru yang menggantung karena walinuxagent memblokir dpkg
CarComp
Ini terlalu hacky (meskipun pada awalnya saya juga menemukan itu).
iBug
2

Siapkan skrip di /etc/rc5.d dengan S99 dan hapus sendiri setelah dijalankan.

mpez0
sumber
2

Anda dapat melakukan ini dengan atperintah, meskipun saya perhatikan Anda tidak dapat (setidaknya pada RHEL 5, yang saya uji coba) menggunakan at @rebootatau at reboot, tetapi Anda dapat menggunakan at now + 2 minutesdan kemudian shutdown -r now.

Ini tidak mengharuskan sistem Anda membutuhkan waktu lebih dari 2 menit untuk berjalan.

Mungkin berguna di mana Anda ingin 0 pengaturan, meskipun saya lebih suka perintah 'runonce' adalah kit standar.

Cameron Kerr
sumber
Berhati-hatilah bahwa jika sistem Anda membutuhkan waktu lebih dari 2 menit hingga atddihentikan, perintah Anda dapat berjalan selama pematian. Ini bisa dihindari dengan menghentikan atdsebelum menjadwalkan perintah dengan atd nowdan kemudian reboot.
mleu
2

Saya pikir jawaban ini adalah yang paling elegan:

Tempatkan skrip /etc/init.d/scriptdan hapus sendiri dengan baris terakhir:rm $0

Kecuali skripnya 100% gagal-bukti, mungkin bijaksana untuk menangani pengecualian untuk menghindari loop kesalahan fatal ..

geotheory
sumber
2

Dulu chkconfigsistem saya secara otomatis menjalankan skrip sekali setelah boot dan tidak pernah lagi. Jika sistem Anda menggunakan ckconfig (Fedora, RedHat, CentOs, dll) ini akan berfungsi.

Pertama skrip:

#!/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. Beri nama skrip run-once
  2. Tempatkan skrip /etc/init.d/
  3. Aktifkan skrip chkconfig run-once on
  4. reboot

Saat sistem melakukan boot, skrip Anda akan berjalan sekali dan tidak pernah lagi.

Yaitu, tidak akan pernah lagi kecuali Anda menginginkannya. Anda selalu dapat mengaktifkan kembali skrip dengan chkconfig run-once onperintah.

Saya suka solusi ini karena menempatkan satu dan hanya satu file pada sistem dan karena perintah run-once dapat dikeluarkan kembali jika diperlukan.

shrewmouse
sumber
-1

dalam sistem redhat dan debian Anda dapat melakukannya dari /etc/rc.local, ini semacam autoexec.bat.

natxo asenjo
sumber
7
Itu akan dieksekusi pada setiap boot tidak hanya yang berikutnya saja.
Dijeda sampai pemberitahuan lebih lanjut.
1
saya buruk, saya salah membaca pertanyaan. Terima kasih atas koreksinya
natxo asenjo