Linux: Schedule command to run once after reboot (RunOnce equivalent)
Solution 1
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.
[email protected]:/home/myuser$ sudo runonce foo
[email protected]:/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
[email protected]:/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 2
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 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 think this answer is the most elegant:
Place script in /etc/init.d/script and self-delete with last line: rm $0
Unless the script is 100% fail-proof, probably wise to handle exceptions to avoid a fatal error loop..
Solution 5
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
- Name the script
run-once - Place the script in
/etc/init.d/ - Enable the script
chkconfig run-once on - 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.
Related videos on Youtube
Christopher Parker
Updated on September 17, 2022Comments
-
Christopher Parker 4 monthsI'd like to schedule a command to run after reboot on a Linux box. I know how to do this so the command consistently runs after every reboot with a
@rebootcrontab entry, however I only want the command to run once. After it runs, it should be removed from the queue of commands to run. I'm essentially looking for a Linux equivalent to RunOnce in the Windows world.In case it matters:
$ 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 = 0Is there an easy, scriptable way to do this?
-
BillThor over 12 yearsRunOnce is an artifact of Windows resulting from problems completing configuration before a reboot. Is there any reason you can't run your script before reboot? The above solution appears to be a reasonable clone of RunOnce. -
Admin 8 monthsI'm surprised there isn't a nice tool for this. Or maybe there is and I haven't discovered it yet.
-
-
Dennis Williamson over 12 yearsThat's going to get executed at each boot not just the next one only. -
Christopher Parker over 12 yearsThanks for your answer. This solution is great. It technically solves my problem, however it seems like there's a lot of preparation of infrastructure required to make this work. It's not portable. I think your solution would ideally be baked into a Linux distribution (I'm not sure why it isn't!). Your answer inspired my ultimate solution, which I've also posted as an answer. Thanks again! -
Dennis Williamson over 12 yearsYour script looks really handy. One thing to note is that it destructively strips comments out of the crontab. -
Christopher Parker over 12 years@Dennis: Thanks. I originally didn't have that extra grep call in there, but all of the comments were piling up; three for every time I ran the script. I think I'll change the script to just always remove comment lines that look like those three auto-generated comments. -
Christopher Parker over 12 years@Dennis: Done. The patterns could probably be better, but it works for me. -
Christopher Parker over 12 yearsWhat made you choose local3, versus any of the other facilities between 0 and 7? -
Dennis Williamson over 12 years@Christopher: A dice roll is always the best method. Seriously, though, for an example it didn't matter and that's the key my finger landed on. Besides, I don't own any eight-sided die. -
Christopher Parker over 12 years@Dennis: Got it, thanks. Coincidentally, local3 is the local facility that appears inman logger. -
Christopher Parker over 12 years@Dennis: Actually, based on crontab.c, I think my patterns are just fine. (Search for "DO NOT EDIT THIS FILE" at opensource.apple.com/source/cron/cron-35/crontab/crontab.c.) -
Andrew Savinykh almost 6 yearsDoes the$filevariable contain full path or just the file name? -
Dennis Williamson almost 6 years@AndrewSavinykh: The full path. -
Andrew Savinykh almost 6 years@DennisWilliamson, thank you, I got this working with systemd instead of cron. Your help is much appreciated ;) -
CarComp about 5 yearsThis is pure genius. Don't forget to chmod +x the /root/runonce.sh. This is perfect for apt-get upgrade on azure machines that hang because walinuxagent blocks the dpkg -
mleu almost 5 yearsBeware that if your system takes longer than 2 minutes untilatdis stopped your command may run during shutdown. This could be avoided by stoppingatdbefore sheduling the command withatd nowand then rebooting. -
iBug over 3 yearsThis is too hacky (though it's also what I came up with at first).