Linux: Schedule command to run once after reboot (RunOnce equivalent)

Solution 1:

Create an @reboot entry in your crontab to run a script called /usr/local/bin/runonce.

Create a directory structure called /etc/local/runonce.d/ran using mkdir -p.

Create the script /usr/local/bin/runonce as follows:

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

Now place any script you want run at the next reboot (once only) in the directory /etc/local/runonce.d and chown and chmod +x it appropriately. Once it's been run, you'll find it moved to the ran subdirectory and the date and time appended to its name. There will also be an entry in your syslog.

Solution 2:

I really appreciate the effort put into Dennis Williamson's answer. I wanted to accept it as the answer to this question, as it is elegant and simple, however:

  • I ultimately felt that it required too many steps to set up.
  • It requires root access.

I think his solution would be great as an out-of-the-box feature of a Linux distribution.

That being said, I wrote my own script to accomplish more or less the same thing as Dennis's solution. It doesn't require any extra setup steps and it doesn't require root access.

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

Save this script (e.g.: runonce), chmod +x, and run:

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

In the event of a typo, you can remove a command from the runonce queue with the -r flag:

$ runonce fop
$ runonce -r fop
$ runonce foo

Using sudo works the way you'd expect it to work. Useful for starting a server just once after the next reboot.

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"

Some notes:

  • This script replicates the environment (PATH, working directory, user) it was invoked in.
  • It's designed to basically defer execution of a command as it would be executed "right here, right now" until after the next boot sequence.

Solution 3:

Create e.g. /root/runonce.sh:

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

Add to /etc/rc.local:

/root/runonce.sh

Solution 4:

I used chkconfig to have my system automatically run a script once after boot and never again. If your system uses ckconfig (Fedora, RedHat, CentOs, etc) this will work.

First the script:

#!/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. Name the script run-once
  2. Place the script in /etc/init.d/
  3. Enable the script chkconfig run-once on
  4. reboot

When the system boots your script will run once and never again.

That is, never again unless you want it to. You can always re-enable the script with the chkconfig run-once on command.

I like this solution because it puts one and only one file on the system and because the run-once command can be re-issued if needed.