75

How do I set up cron to run a file just once at a specific time? One of the alternatives is at but it is not accessible to all users on standard hosting plans. Therefore I was wondering whether there is way to do it using cron?

Boann
  • 48,794
  • 16
  • 117
  • 146
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • I have tried running `$at = shell_exec('at');` on standart LAMP server and it returned NULL. Therefore I asume it doesn't work (have permissions) by default. – Gajus Mar 29 '11 at 14:05
  • Is there an easy way to move this to ServerFault? It seems more appropriate there. – TomOnTime Aug 23 '22 at 17:36

7 Answers7

85

You really want to use at. It is exactly made for this purpose.

echo /usr/bin/the_command options | at now + 1 day

However if you don't have at, or your hosting company doesn't provide access to it, you can have a cron job include code that makes sure it only runs once.

Set up a cron entry with a very specific time:

0 0 2 12 * /home/adm/bin/the_command options

Next /home/adm/bin/the_command needs to either make sure it only runs once.

#! /bin/bash

COMMAND=/home/adm/bin/the_command
DONEYET="${COMMAND}.alreadyrun"

export PATH=/usr/bin:$PATH

if [[ -f $DONEYET ]]; then
  exit 1
fi
touch "$DONEYET"

# Put the command you want to run exactly once here:
echo 'You will only get this once!' | mail -s 'Greetings!' me@example.com
TomOnTime
  • 4,175
  • 4
  • 36
  • 39
  • The + is for "You really want to use at. It is exactly made for this purpose.", because that's so true... I did only just find out about it though; so, why not use it's own CLI? Unless the current interaction perhaps added after 2015, in which case my question isn't really applicable.. –  Jun 19 '17 at 18:38
  • 2
    You don't have to `echo` the command into "at". Its just something I do because eventually I automate everything. – TomOnTime Jun 19 '17 at 19:04
  • Hi @Tomontime, I wonder if one can trigger reboot like this (without cron), since that needs sudo/root - like `echo "reboot" | at now + 9 hour` – Valter Ekholm Dec 22 '20 at 15:51
  • 1
    @ValterEkholm You can do that. However you might get better results by using `shutdown` and specifying the right flags to reboot, and to delay by 9 hours. The benefit of using the shutdown command is that it has built-in facilities to abort a reboot; people might be more familiar with those flags than `atq`. The downside is that `shutdown` has different flags on Linux, *BSD, Solaris, etc. – TomOnTime Dec 23 '20 at 16:22
  • Aha, thanks, now I get the idea - not use "at" / just "shutdown with flags" – Valter Ekholm Dec 27 '20 at 18:43
45

Try this out to execute a command on 30th March 2011 at midnight:

0 0 30 3 ? 2011  /command

WARNING: As noted in comments, the year column is not supported in standard/default implementations of cron. Please refer to TomOnTime answer below, for a proper way to run a script at a specific time in the future in standard implementations of cron.

egelev
  • 1,175
  • 2
  • 11
  • 27
gparis
  • 1,247
  • 12
  • 32
  • so this will make it run only once? well, seemingly. However, is there any way to convert timestamp to crong time format? – Gajus Mar 29 '11 at 14:13
  • You should probably omit the 'Day of week' field by using a question mark (?). – gparis Mar 29 '11 at 14:43
  • 2
    From Wikipedia: http://en.wikipedia.org/wiki/Cron#Special_Characters ... The '?' character is used to omit the specification of a value for the day-of-month and day-of-week fields. Since it's not valid to specify values for both fields, '?' becomes necessary to omit one of either ('*' is considered a specific value). – gparis Mar 29 '11 at 15:04
  • If I'm correct, does this add a line every time you execute this? Or will it remove itself from the crontab list after it's done? – Martijn Oct 21 '14 at 13:52
  • You may edit the crontab file and remove the scheduled job after it has been executed. – gparis Oct 21 '14 at 14:32
  • 4
    Huh? When did "year" get added as a column. Can you explain? – TomOnTime Sep 04 '15 at 00:56
  • 2
    There is no year in "standard" crontab. But checkout this question: http://stackoverflow.com/questions/7724316/how-can-i-specify-time-in-cron-considering-year - some implementations do allow it. – Matthew Wilcoxson Dec 07 '15 at 11:17
  • This will run on March 30 every year for normal "cron"s without a year specification. https://crontab.guru/#0_0_30_3_* – Katie Aug 08 '18 at 17:00
  • Downvoted. This answer does not run once, as the original question requests. – Sopalajo de Arrierez Jul 25 '19 at 19:33
28

You really want to use at. It is exactly made for this purpose.

echo /usr/bin/the_command options | at now + 1 day

However if you don't have at, or your hosting company doesn't provide access to it, you could make a self-deleting cron entry.

Sadly, this will remove all your cron entries. However, if you only have one, this is fine.

0 0 2 12 * crontab -r ; /home/adm/bin/the_command options

The command crontab -r removes your crontab entry. Luckily the rest of the command line will still execute.

WARNING: This is dangerous! It removes ALL cron entries. If you have many, this will remove them all, not just the one that has the "crontab -r" line!

Pang
  • 9,564
  • 146
  • 81
  • 122
TomOnTime
  • 4,175
  • 4
  • 36
  • 39
  • 5
    Moderator note: this is not a repost of [Tom's other answer here](https://stackoverflow.com/a/30378470/100297), this is an alternative answer using a different technique. – Martijn Pieters Nov 28 '16 at 16:57
4

You could put a crontab file in /etc/cron.d which would run a script that would run your command and then delete the crontab file in /etc/cron.d. Of course, that means your script would need to run as root.

Srini V
  • 11,045
  • 14
  • 66
  • 89
Brian Showalter
  • 4,321
  • 2
  • 26
  • 29
1

Your comment suggests you're trying to call this from a programming language. If that's the case, can your program fork a child process that calls sleep then does the work?

What about having your program calculate the number of seconds until the desired runtime, and have it call shell_exec("sleep ${secondsToWait) ; myCommandToRun");

Srini V
  • 11,045
  • 14
  • 66
  • 89
dj_segfault
  • 11,957
  • 4
  • 29
  • 37
  • You are right: I am trying to run it from PHP. What if the secondsToWait is [..] 60*60*24*30, that is, 30 days? wouldn't it eventually kill the server? (and I need to start more than one job; hundreds [but they must run at different times]) – Gajus Mar 29 '11 at 14:16
  • You didn't say anything in your question about the time being a month away. Of course that changes things. – dj_segfault Mar 31 '11 at 04:56
1

For those who is not able to access/install at in environment, can use custom script:

#!/bin/bash
if [ $# -lt 2 ]; then
echo ""
echo "Syntax Error!"
echo "Usage: $0 <shell script> <datetime>"
echo "<datetime> format: %Y%m%d%H%M"
echo "Example: $0 /home/user/scripts/server_backup.sh 202008142350"
echo ""
exit 1
fi

while true; do
  t=$(date +%Y%m%d%H%M);      
  if [ $t -eq $2 ]; then
    /bin/bash $1
    echo DONE $(date);
    break;
  fi;
  sleep 1;
done

Let's name the script as run1time.sh Example could be something like:

nohup bash run1time.sh /path/to/your/script.sh 202008150300 &
Zaur Hajili
  • 29
  • 1
  • 2
0

at is the correct way.

If you don't have the at command in the machine and you also don't have install privilegies on it, you can put something like this on cron (maybe with the crontab command):

* * * 5 * /path/to/comand_to_execute; /usr/bin/crontab -l | /usr/bin/grep -iv command_to_execute | /usr/bin/crontab - 

it will execute your command one time and remove it from cron after that.

alexandre1985
  • 1,056
  • 3
  • 13
  • 31