schedule is created to mimic linux’s “at” function which allows you to schedule a command/script/set of commands to run later.

schedule is a bash function (that you need to copy paste into your shell, or run from bash_profile/bashrc file)
since its function it will only last thru your shell and its subshells it will not interrupt other shells

I wrote a bash function called schedule, very basic style. Its like linuxes “at” command, which allows you to schedule a command for later. My version of FreeBSD is missing it (im using FreeBSD 8), im sure I Could install it somehow, but I just wrote a bash function to compensate.

BEFORE USING: edit these 2 items in the bash function.

  1. edit CRONTABFILE=”/var/cron/tabs/root” to match the cron file that is accessed when you run crontab -e/crontab -l.
  2. edit /etc/rc.d/cron restart in the if statement towards the bottom (cron needs to be restarted so that it takes on your changes). Note that when you edit a crontab with crontab -e, it automatically reloads the crontab. I didnt find another way to reload the crontab other than restarting cron – shouldn’t be a big deal to restart cron.

The bash function (Tested with FreeBSD 8 and bash 4.x)

unset schedule; function schedule () {
  ## run without args to see what this does
  ## CONTENTS is the contents of FILE. FILE is the script thats ran at CRONTIME by CRONTABFILE.
  ## URL: https://paste.sh/Eg3ov_Ru#XRLJCoOEbffPrtS-cG8j_ARG
  ## -- check and show usage -- ##
  [ $# -eq 0 ] && { echo -e "\nschedule replaces linux's 'at' command, as it does not exist in this OS, it allows you to run a command[s] at a later time. schedule is a bash function which runs a task at given time & it will only run once. After running once the crontab line gets removed from crontab.\n* usage: schedule 'MIN HR DAY MON DOW' '/bin/sometask'\n  MIN: minute (0-60), HR: hour (0-23), DAY: day of month (1-31), MON: month (1-12), DOW: day of week (0-6, sunday = 0)\n* Example: schedule '15 18 * * *' '/bin/pzpatch.sh -a PATCH1'   # adds patch PATCH1 at 6:15pm today and thats it\n* CAUTION: keep in mind MIN comes before HR. Also if you use \\\t or \\\n in command they might get interpretted as tab or newline respectively, so dont forget to escape as needed\n* TIP: if just need to run a command once just set the HR and MIN and the remaining as astericks (like in the example). It will not run every day at HR:MIN time, it will only run once\n* TIP: if you have to run several commands to run, put them in a script, and set 2nd argument to the full path of script\n* TIP: try to use full paths instead of relative paths, and/or edit THEPATHS variable to meet your environments needs for paths"; return 1; }
  ## -- local vars -- ##
  local CRONTABFILE THESCRIPT CRONTIME COMMAND CRONLINE TMPFILE THEPATHS
  ## -- set variables -- ##
  CRONTIME="$1"
  COMMAND="$2"
  THESCRIPT="/tmp/schedule-script-`date +%s`-${RANDOM}.sh"
  TMPFILE="/tmp/schedule-bkp-`date +%Y%m%d`-${RANDOM}"
  CRONLINE="${CRONTIME} ${THESCRIPT}"
  CRONTABFILE="/var/cron/tabs/root"  ## this is whats accessed with crontab -e / -l
  THEPATHS="PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/bin/X11:/usr/local/bin:/usr/local/sbin;" # edit for your env, run "echo $PATH" to help set this value.
  # THEPATHS="" # you can leave THEPATHS blank, but then make sure to use full paths to all of your commands 
  ## -- show what the changes are going to be and ask if you want to apply -- ##
  echo -e "\nSchedule the following: at time '${CRONTIME}', run command:\n${COMMAND}\n\nAllow me to create '$THESCRIPT' which will be scheduled to run using cron at '$CRONTIME'"
  echo "Also allow me append to the main crontab file, $CRONTABFILE, the following line:"
  echo "${CRONLINE}"
  echo "SIDENOTE: backups of the current crontab will be made to ${TMPFILE}.bak0 before any changes"
  echo "Contents of '${THESCRIPT}' (this will be run at time ${CRONTIME}):"
  ## -- this CONTENTS variable will have a whole script in there -- ##
  CONTENTS="############### START ################\n${THEPATHS}\n######################################\n${COMMAND}\n######################################\n# run once and not again, remove self from crontab:\ncp '/var/cron/tabs/root' '${TMPFILE}.bak1' # make backup;\ncat '${CRONTABFILE}' | grep -v '${THESCRIPT}' > '${TMPFILE}.final' # save crontab file without our task;\ncp '${TMPFILE}.final' '${CRONTABFILE}' # copy our new crontab to the main crontab file\n# clean up the script and backup files - currently commented out\n# rm -f '${THESCRIPT}' '${TMPFILE}.bak0' '${TMPFILE}.bak1' '${TMPFILE}.final'\n################ END #################"
  ## -- show us the CONTENTS variable as it would look like in a script form (convert \n to newlines) -- ##
  echo ""
  echo -e $CONTENTS
  ## -- allow to proceed or stop yes or no-- ##
  echo ""
  read -r -p "* Allow above request? ([y]es/[n]o, default no)? " response
  echo ""
  response=`echo "$response" | awk '{print tolower($0)}'`
  case "$response" in
     "y" | "ye" | "yes") iCONTINUE=1 ;;
     *) iCONTINUE=0 ;;
  esac
  ## -- if yes then continue with top part of if -- ##
  if [ $iCONTINUE -eq 1 ]; then
     ## -- let user know what we are doing -- ##
     ## -- make CONTENTS into script file in /tmp -- ##
     ## -- backup crontab into file in /tmp -- ##
     ## -- append our crontab line to the current crontab file -- ##
     ## -- that crontab line will be grepped out when our CONTENTS script is ran -- ##
     ## -- everything will revert back to normal after that -- ##
     echo "* Making script '$THESCRIPT' to run your command:"
     echo -e $CONTENTS > "$THESCRIPT"
     chmod +x "$THESCRIPT"
     echo "* Backing up current crontab for root"
     cp "${CRONTABFILE}" "${TMPFILE}.bak0"
     echo "* Adding / Appending task to crontab"
     echo "$CRONLINE" >> "$CRONTABFILE"
     echo "* restarting cron, so that cron is aware of new task"
     /etc/rc.d/cron restart ## without this cron will not take place (unless we go in to crontab -e and exit out)
     echo "*** DONE - ready to go ***"
     echo "* Verify current crontab with (it will be at the bottom):"
     echo "crontab -l"      
     echo "* Verify script that will run at time'$1' with:"
     echo -e "cat $THESCRIPT\n"
  else
     echo -e "* STOPPED - didnt do anything\n"
  fi
}

Copy paste that into your bash session and you will have that “schedule” function.

Just type “schedule” in bash to see how to use it:

# schedule

Run a task at given time & it will only run once (after running once the crontab line gets removed from crontab).
* usage: schedule 'MIN HR DAY MON DOW' '/bin/sometask'
  MIN: minute (0-60), HR: hour (0-23), DAY: day of month (1-31), MON: month (1-12), DOW: day of week (0-6, sunday = 0)
* Example: schedule '15 18 * * *' '/bin/pzpatch.sh -a PATCH1'   # adds patch PATCH1 at 6:15pm today and thats it
* CAUTION: keep in mind MIN before HR. Also if you use \t or \n in command they might get interpretted as tab or newline respectively, so dont forget to escape as needed
* TIP: if just need to run it once just set HR and MIN and the remaining as astericks, it will not run every day at HR:MIN time, it will only run once
* TIP: if you have to run several commands to run, put them in a script, and set 2nd argument to the full path of script
* TIP: try to use full paths instead of relative paths

To schedule something like “(date; df -h) >> /tmp/dfsize.txt” to run at the next 19:20pm (localtime to your box)

schedule '20 19 * * *' '(date; df -h) >> /tmp/dfsize.txt'

That will create a script in /tmp/crontab-EPOCHTIME-somerandomnumber.sh (that has +x executable bit so it can run). That script runs your (date; df -h) >> /tmp/dfsize.txt and then it removes the job from cron.

After that script is created. It creates the following crontab line to your crontab

20 19 * * * /tmp/crontab-EPOCHTIME-somerandomnumber.sh

So then at 19:20 (7:20pm) this script runs /tmp/crontab-EPOCHTIME-somerandomnumber.sh which runs your command and then deletes that crontab line (thus ensuring it doesnt get deleted).

NOTE: it deletes it by grepping out /tmp/crontab-EPOCHTIME-somerandomnumber.sh (which should be unique thanks to EPOCHTIME and RANDOM)

The End.

Leave a Reply

Your email address will not be published. Required fields are marked *