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.
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 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 over 1 year
I'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
@reboot
crontab 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 = 0
Is there an easy, scriptable way to do this?
-
BillThor almost 14 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 almost 2 yearsI'm surprised there isn't a nice tool for this. Or maybe there is and I haven't discovered it yet.
-
-
Dennis Williamson almost 14 yearsThat's going to get executed at each boot not just the next one only.
-
Christopher Parker almost 14 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 almost 14 yearsYour script looks really handy. One thing to note is that it destructively strips comments out of the crontab.
-
Christopher Parker almost 14 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 almost 14 years@Dennis: Done. The patterns could probably be better, but it works for me.
-
Christopher Parker almost 14 yearsWhat made you choose local3, versus any of the other facilities between 0 and 7?
-
Dennis Williamson almost 14 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 almost 14 years@Dennis: Got it, thanks. Coincidentally, local3 is the local facility that appears in
man logger
. -
Christopher Parker almost 14 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 about 7 yearsDoes the
$file
variable contain full path or just the file name? -
Dennis Williamson about 7 years@AndrewSavinykh: The full path.
-
Andrew Savinykh about 7 years@DennisWilliamson, thank you, I got this working with systemd instead of cron. Your help is much appreciated ;)
-
CarComp over 6 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 about 6 yearsBeware that if your system takes longer than 2 minutes until
atd
is stopped your command may run during shutdown. This could be avoided by stoppingatd
before sheduling the command withatd now
and then rebooting. -
iBug almost 5 yearsThis is too hacky (though it's also what I came up with at first).