How can I make a bash command run periodically?
I want to execute a script and have it run a command every x minutes. Also any general advice on any resources for learning bash scripting could be really cool. I use Linux for my personal development work, so bash scripts are not totally foreign to me, I just haven’t written any of my own from scratch.
As for your request for resources, the bash-hackers wiki has a well-tended list of resources, which is explicitly pruned to avoid guides that are not careful to avoid classic pitfalls.
9 Answers 9
If you want to run a command periodically, there’s 3 ways :
- using the crontab command ex. * * * * * command (run every minutes)
- using a loop like : while true; do ./my_script.sh; sleep 60; done (not precise)
- using systemd timer
Some pointers for best bash scripting practices :
I don’t know about better and worse choices. They both have their pros and cons. One downside of the cron approach is you can get multiple instances of the command running at the same time. That cannot happen with the foreground while loop example.
Really depends of what you want to do. The while loop will get out of sync (with whatever). If you have to be time acurate the loop wouldn’ t work (at least not that easy).
Cron is of course what should have been obvious to me at the beginning. This is a process that will run once every fifteen or thirty minutes and will only take seconds to complete.
In addition to @sputnick’s answer, there is also watch . From the man page:
Execute a program periodically, showing output full screen
By default this is every 2 seconds. watch is useful for tail ing logs, for example.
It’s important to point out that watch is nonstandard and doesn’t exist on most non-GNU systems (including Macs) by default.
macOS users: here’s a partial implementation of the GNU watch command (as of version 0.3.0 ) for interactive periodic invocations for primarily visual inspection:
It is syntax-compatible with the GNU version and fails with a specific error message if an unimplemented feature is used.
- The output is not limited to one screenful.
- Displaying output differences is not supported.
- Using precise timing is not supported.
- Colored output is always passed through ( —color is implied).
Also implements a few non-standard features, such as waiting for success ( -E ) to complement waiting for error ( -e ) and showing the time of day of the last invocation as well as the total time elapsed so far.
watch -n 1 ls # list current dir every second watch -e 'ls *.lockfile' # list lock files and exit once none exist anymore.
Source code (paste into a script file named watch , make it executable, and place in a directory in your $PATH ; note that syntax highlighting here is broken, but the code works):
#!/usr/bin/env bash THIS_NAME=$(basename "$BASH_SOURCE") VERSION='0.1' # Helper function for exiting with error message due to runtime error. # die [errMsg [exitCode]] # Default error message states context and indicates that execution is aborted. Default exit code is 1. # Prefix for context is always prepended. # Note: An error message is *always* printed; if you just want to exit with a specific code silently, use `exit n` directly. die() < echo "$THIS_NAME: ERROR: $" 1>&2 exit $ # Note: If the argument is non-numeric, the shell prints a warning and uses exit code 255. > # Helper function for exiting with error message due to invalid parameters. # dieSyntax [errMsg] # Default error message is provided, as is prefix and suffix; exit code is always 2. dieSyntax() < echo "$THIS_NAME: PARAMETER ERROR: $Use -h for help." 1>&2 exit 2 > # Get the elapsed time since the specified epoch time in format HH:MM:SS. # Granularity: whole seconds. # Example: # tsStart=$(date +'%s') # . # getElapsedTime $tsStart getElapsedTime() < date -j -u -f '%s' $(( $(date +'%s') - $1 )) +'%H:%M:%S' ># Command-line help. if [[ "$1" == '--help' || "$1" == '-h' ]]; then cat ) for (( i = 2; i < $; i++ )); do params+=("-$") done else (( argsReached && ! decompressed )) && break [[ $p == '--' || $ != '-' ]] && argsReached=1 params+=("$p") fi done (( decompressed )) && set -- "$"; unset params decompressed argsReached p # Replace "$@" with the expanded parameter set. # Option-parameters loop. interval=2 # default interval runUntilFailure=0 runUntilSuccess=0 quietStdOut=0 quietStdOutAndStdErr=0 dontClear=0 noHeader=0 beepOnErr=0 useExec=0 while (( $# )); do case "$1" in --) # Explicit end-of-options marker. shift # Move to next param and proceed with data-parameter analysis below. break ;; -p|--precise|-d|--differences|--differences=*) dieSyntax "Sadly, option $1 is NOT IMPLEMENTED." ;; -v|--version) echo "$VERSION"; exit 0 ;; -x|--exec) useExec=1 ;; -c|--color) # a no-op: unlike the GNU version, we always - and invariably - pass color codes through. ;; -b|--beep) beepOnErr=1 ;; -l|--list) dontClear=1 ;; -e|--errexit) runUntilFailure=1 ;; -E|--okexit) runUntilSuccess=1 ;; -n|--interval) shift; interval=$1; errMsg="Please specify a positive number of seconds as the interval." interval=$(bc <<<"$1") || dieSyntax "$errMsg" (( 1 == $(bc <<<"$interval >0") )) || dieSyntax "$errMsg" [[ $interval == *.* ]] || interval+='.0' ;; -t|--no-title) noHeader=1 ;; -q|--quiet) quietStdOut=1 ;; -Q|--quiet-both) quietStdOutAndStdErr=1 ;; -?|--?*) # An unrecognized switch. dieSyntax "Unrecognized option: '$1'. To force interpretation as non-option, precede with '--'." ;; *) # 1st data parameter reached; proceed with *argument* analysis below. break ;; esac shift done # Make sure we have at least a command name [[ -n "$1" ]] || dieSyntax "Too few parameters specified." # Suppress output streams, if requested. # Duplicate stdout and stderr first. # This allows us to produce output to stdout (>&3) and stderr (>&4) even when suppressed. exec 3 /dev/null elif (( quietStdOut )); then exec 1> /dev/null fi # Set an exit trap to ensure that the duplicated file descriptors are closed. trap 'exec 3>&- 4>&-' EXIT # Start loop with periodic invocation. # Note: We use `eval` so that compound commands - e.g. 'ls; bash --version' - can be passed. tsStart=$(date +'%s') while :; do (( dontClear )) || clear (( noHeader )) || echo "Every $s. [$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)]: $@"$'\n' >&3 if (( useExec )); then (exec "$@") # run in *subshell*, otherwise *this* script will be replaced by the process invoked else if [[ $* == *' '* ]]; then # A single argument with interior spaces was provided -> we must use `bash -c` to evaluate it properly. bash -c "$*" else # A command name only or a command name + arguments were specified as separate arguments -> let bash run it directly. "$@" fi fi ec=$? (( ec != 0 && beepOnErr )) && printf '\a' (( ec == 0 && runUntilSuccess )) && < echo $'\n'"[$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)] Exiting as requested: exit code 0 reported." >&3; exit 0; > (( ec != 0 && runUntilFailure )) && < echo $'\n'"[$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)] Exiting as requested: non-zero exit code ($ec) reported." >&3; exit 0; > sleep $interval done
How do run a Unix command at a given time interval?
I have a Unix script. In that script I have a command called «ls». I want that «ls» command to run every 5 minutes from that script.
5 Answers 5
Use watch . The -n flag specifies interval in seconds, so
Unless of course you want this to happen every five minutes always. That would be a job for cron, but if you just want to do it occasionally, I’d recommend using watch .
while true; do ls sleep 300 done
Well most people are suggesting you to use cron and such. but if I got the question right, it looks like you already have a script and want the ls every 5 minutes within the script. So again, if I’m right, cron doesn’t apply here.
Put your script into the Crontab via
More information about Cron you can find at wikipedia
You could use crontab for example. See http://en.wikipedia.org/wiki/Cron
for example i you want to run your script every five minutes via crontab it should look something like this:
#m h dom mon dow user command */5 * * * * root /path/to/script
Linked
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
Site design / logo © 2023 Stack Exchange Inc; user contributions licensed under CC BY-SA . rev 2023.7.12.43529
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
Run a command at a specific time
I’m trying to run a command at a specific time. I’ve looked at the «at» command, but I don’t know how to get it working. Here’s what I do:
at 1843 (Enter) php /run/this/script.php (Ctrl+D)
But how do I do this in a bash script? I mean, I need to press enter and «Ctrl+D» to set up the delay. How to do this in a script? Any suggestions most welcome. Thanks in advance,
A specific time is a kind of interval, if you want it at that time every day or at the beginning of the month. Also, sometimes it makes more sense to have it as a regular cron job which checks to see if it needs to run, instead of having some script schedule it; what happens if the script is inadvertently twice?
Late to the party, but if you’d like to run the ‘at’ command the same way you’d run the sleep command whilst using a specific time instead of duration, then you set the following (which I use myself) in your shell’s rc file: at() )(2)/\1:\2/’ <(echo $1)) | sed -r 's/s$//'`>– Example: at 15:10; ls or at 1510; ls
5 Answers 5
You can echo your command into at as input:
echo "/usr/bin/php /run/this/script.php" | at 18:43
Hey, thanks for that. I tried to run firefox at 1847, but it didn’t work. Here’s the command: echo «/usr/bin/firefox» | at 1847
Hey, I tried as 18:47 too, both syntaxes appear to be valid and are added to the job queue. Firefox doesn’t come up though.
My test didn’t work either with firefox. That may have to do with X server environment. Your initial example with a PHP script would work though.
@user22180 Jobs scheduled with at are assigned a job id, which you can find by running atq or reading the output from at , which would be something like job 22 at . . Then use the atrm command to remove that job from the queue by its id. Check the man pages: man at
edit if what you want to do is run Firefox, try this:
Well when «at» runs the program, it may be that it doesn’t have access to the X display; you might try making sure that the «DISPLAY» environment variable is set to «:0.0» or something inside the «at» script, before you run the browser.
In bash or zsh you can say
Failing that, you need to use a here document:
You might also want to look into cron for regularly scheduled jobs; the crontab entry would look like
43 18 * * * php /run/this/script.php
(EDIT: whoops, helps to recall which version of at . I think that may have been a local mod.)
No problem, you wouldn’t know how to open firefox at a future time would you by any chance? i.e. /usr/bin/firefox isn’t working.
Neither at nor cron normally has access to the window system. You should be able to get the values of $DISPLAY and $XAUTHORITY and set then in the job with env ; often just env DISPLAY=:0 /usr/bin/firefox will work as the command.
brilliant! Thanks so much for that. I got it working by adding the line export DISPLAY=:0.0 to my script.
echo «php /run/this/script.php» | at 18:43
The at command is not installed by default to the systems I work (Ubuntu, RHEL) and I don’t have permissions to install new software, so I use scripts that combine bash and awk in order to achieve the at functionality as follows:
#! /bin/bash res="" while [ ! $res ]; do res=$( date | awk -v hour=$1 -v min=$2 ' < split($4, tmp, ":" ); if( (tmp[1] == hour) && (tmp[2] == min) ) < print "ok"; >else < print ""; >>' ) done
./atReplacement.sh $HOUR $MIN; [OTHER_COMMANDS]
Why don’t you use sleep ? If I understand well, your script will run two commands ( date and awk ) again and again until the expected time! You should rather compute the remaining time to wait and use sleep instead.