Linux: Lập lịch lệnh để chạy một lần sau khi khởi động lại (tương đương RunOnce)


26

Tôi muốn lên lịch để chạy lệnh sau khi khởi động lại trên hộp Linux. Tôi biết cách thực hiện việc này để lệnh chạy liên tục sau mỗi lần khởi động lại với @rebootmục crontab, tuy nhiên tôi chỉ muốn lệnh chạy một lần. Sau khi chạy, nó sẽ bị xóa khỏi hàng lệnh để chạy. Về cơ bản, tôi đang tìm kiếm một Linux tương đương với RunOnce trong thế giới Windows.

Trong trường hợp có vấn đề:

$ 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

Có một cách dễ dàng, kịch bản để làm điều này?


1
RunOnce là một tạo tác của Windows do các vấn đề hoàn thành cấu hình trước khi khởi động lại. Có bất kỳ lý do nào bạn không thể chạy tập lệnh của mình trước khi khởi động lại? Giải pháp trên dường như là một bản sao hợp lý của RunOnce.
BillThor

Câu trả lời:


38

Tạo một @rebootmục trong crontab của bạn để chạy một tập lệnh được gọi /usr/local/bin/runonce.

Tạo một cấu trúc thư mục được gọi là /etc/local/runonce.d/ransử dụng mkdir -p.

Tạo tập lệnh /usr/local/bin/runoncenhư sau:

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

Bây giờ đặt bất kỳ kịch bản mà bạn muốn chạy lúc khởi động lại tiếp theo (một lần duy nhất) trong thư mục /etc/local/runonce.dchownchmod +xnó một cách thích hợp. Khi nó được chạy, bạn sẽ thấy nó được chuyển đến ranthư mục con và ngày và giờ được gắn vào tên của nó. Cũng sẽ có một mục trong của bạn syslog.


2
Cảm ơn câu trả lời của bạn. Giải pháp này là tuyệt vời. Về mặt kỹ thuật, nó giải quyết được vấn đề của tôi, tuy nhiên có vẻ như có rất nhiều sự chuẩn bị về cơ sở hạ tầng cần thiết để thực hiện công việc này. Nó không phải là di động. Tôi nghĩ rằng giải pháp của bạn lý tưởng nhất sẽ được đưa vào một bản phân phối Linux (tôi không chắc tại sao nó lại không!). Câu trả lời của bạn đã truyền cảm hứng cho giải pháp cuối cùng của tôi, mà tôi cũng đã đăng lên như một câu trả lời. Cảm ơn một lần nữa!
Christopher Parker

Điều gì khiến bạn chọn local3, so với bất kỳ cơ sở nào khác trong khoảng từ 0 đến 7?
Christopher Parker

3
@Christopher: Cuộn xúc xắc luôn là phương pháp tốt nhất. Mặc dù vậy, nghiêm túc, ví dụ, nó không thành vấn đề và đó là chìa khóa mà ngón tay tôi chạm vào. Ngoài ra, tôi không sở hữu bất kỳ cái chết tám mặt nào.
Tạm dừng cho đến khi có thông báo mới.

@Dennis: Hiểu rồi, cảm ơn. Thật trùng hợp, local3 là cơ sở địa phương xuất hiện man logger.
Christopher Parker

Liệu $filebiến chứa đường dẫn đầy đủ hay chỉ là tên file?
Andrew Savinykh

21

Tôi thực sự đánh giá cao nỗ lực đưa vào câu trả lời của Dennis Williamson . Tôi muốn chấp nhận nó như là câu trả lời cho câu hỏi này, tuy nhiên nó thanh lịch và đơn giản:

  • Cuối cùng tôi cảm thấy rằng nó đòi hỏi quá nhiều bước để thiết lập.
  • Nó đòi hỏi quyền truy cập root.

Tôi nghĩ rằng giải pháp của anh ấy sẽ tuyệt vời như một tính năng vượt trội của bản phân phối Linux.

Điều đó đang được nói, tôi đã viết kịch bản của riêng mình để thực hiện ít nhiều điều tương tự như giải pháp của Dennis. Nó không yêu cầu bất kỳ bước thiết lập bổ sung nào và nó không yêu cầu quyền truy cập 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

Lưu tập lệnh này (ví dụ runonce:) chmod +x, và chạy:

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

Trong trường hợp lỗi chính tả, bạn có thể xóa lệnh khỏi hàng đợi runonce với cờ -r:

$ runonce fop
$ runonce -r fop
$ runonce foo

Sử dụng sudo hoạt động theo cách bạn mong đợi nó hoạt động. Hữu ích để bắt đầu một máy chủ chỉ một lần sau lần khởi động lại tiếp theo.

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"

Một số lưu ý:

  • Kịch bản này sao chép môi trường (PATH, thư mục làm việc, người dùng) mà nó được gọi.
  • Về cơ bản, nó được thiết kế để trì hoãn việc thực thi một lệnh vì nó sẽ được thực thi "ngay tại đây, ngay bây giờ" cho đến sau chuỗi khởi động tiếp theo.

Kịch bản của bạn trông thực sự tiện dụng. Một điều cần lưu ý là nó phá hủy các bình luận ra khỏi crontab.
Tạm dừng cho đến khi có thông báo mới.

@Dennis: Cảm ơn. Ban đầu tôi không có cuộc gọi grep thêm ở đó, nhưng tất cả các ý kiến ​​đều chồng chất; ba cho mỗi lần tôi chạy kịch bản. Tôi nghĩ rằng tôi sẽ thay đổi tập lệnh để luôn loại bỏ các dòng bình luận trông giống như ba bình luận được tạo tự động đó.
Christopher Parker

@Dennis: Xong rồi. Các mô hình có thể tốt hơn, nhưng nó hoạt động với tôi.
Christopher Parker

@Dennis: Trên thực tế, dựa trên crontab.c, tôi nghĩ rằng mô hình của tôi là tốt. (Tìm kiếm "KHÔNG CHỈNH SỬA NÀY" tại opensource.apple.com/source/cron/cron-35/crontab/crontab.c. )
Christopher Parker

5

Tạo ví dụ /root/runonce.sh:

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

Thêm vào /etc/rc.local:

/root/runonce.sh

Đây là thiên tài thuần túy. Đừng quên chmod + x /root/runonce.sh. Điều này là hoàn hảo để nâng cấp apt-get trên các máy azure bị treo vì walinuxagent chặn dpkg
CarComp

Điều này quá khó khăn (mặc dù đó cũng là những gì tôi nghĩ ra lúc đầu).
iBug

2

Thiết lập tập lệnh trong /etc/rc5.d với S99 và xóa nó sau khi chạy.


2

Bạn có thể thực hiện điều này bằng atlệnh, mặc dù tôi nhận thấy bạn không thể (ít nhất là trên RHEL 5, mà tôi đã thử nghiệm) sử dụng at @reboothoặc at reboot, nhưng bạn có thể sử dụng at now + 2 minutesvà sau đó shutdown -r now.

Điều này không yêu cầu hệ thống của bạn mất hơn 2 phút để chạy.

Nó có thể hữu ích khi bạn muốn thiết lập 0, mặc dù tôi không muốn lệnh 'runonce' là bộ tiêu chuẩn.


Xin lưu ý rằng nếu hệ thống của bạn mất hơn 2 phút cho đến khi atdbị dừng thì lệnh của bạn có thể chạy trong khi tắt máy. Điều này có thể tránh được bằng cách dừng lại atdtrước khi hủy lệnh bằng cách atd nowkhởi động lại.
mleu

2

Tôi nghĩ rằng câu trả lời này là thanh lịch nhất:

Đặt tập lệnh vào /etc/init.d/scriptvà tự xóa với dòng cuối cùng:rm $0

Trừ khi tập lệnh không chứng minh được 100%, có lẽ nên khôn ngoan để xử lý các trường hợp ngoại lệ để tránh vòng lặp lỗi nghiêm trọng ..


2

Tôi chkconfigđã từng có hệ thống của mình tự động chạy tập lệnh một lần sau khi khởi động và không bao giờ trở lại. Nếu hệ thống của bạn sử dụng ckconfig (Fedora, RedHat, CentOs, v.v.) thì nó sẽ hoạt động.

Đầu tiên là kịch bản:

#!/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. Đặt tên cho kịch bản run-once
  2. Đặt tập lệnh vào /etc/init.d/
  3. Kích hoạt tập lệnh chkconfig run-once on
  4. khởi động lại

Khi hệ thống khởi động, tập lệnh của bạn sẽ chạy một lần và không bao giờ lặp lại.

Đó là, không bao giờ một lần nữa trừ khi bạn muốn nó. Bạn luôn có thể kích hoạt lại tập lệnh bằng chkconfig run-once onlệnh.

Tôi thích giải pháp này vì nó đặt một và chỉ một tệp trên hệ thống và vì lệnh chạy một lần có thể được cấp lại nếu cần.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.