Bash while loop between two times

Iz3k34l picture Iz3k34l · Jun 4, 2013 · Viewed 8.9k times · Source

I am trying to write a bash script that will loop if the time is after certain time and before a certain time. Here is what i have got but it gives me an error;

#!/bin/bash

while [ date +"%T" -gt '06:00:00' && date +"%T" -lt '21:00:00'];
  do
  ##echo `php mainstatquery.php`
  echo "Hello World";
  sleep 5;
done

and this is the error

./timer.sh: line 3: [: missing `]'

If anyone could point me in the right direction.

Answer

David W. picture David W. · Jun 5, 2013

Here's an easy way to understand your while problems:

The [ is an actual Unix command. Do ls /bin/[ and you'll see. It's a link to the /bin/test command. Do a man test from the command line and see the various tests that can be done.

What the while does is execute the command you give it, and if it returns a zero exit status, the while statement is considered true and the statement will continue. You can do things like this:

while sleep 2
do
   echo "I slept for two seconds"
done

All the test command does is do some sort of testing (file tests, tests of equality, etc.) and return a zero if the statement is true and a non-zero otherwise:

$  test 2 -gt 3
$ echo $?
1   <- That statement is false

$ [ 2 -lt 3 ]   #The test command using square brackets
$ echo $?
0  <- That statement is true.

Take a look at the manpage for test and you'll see all valid tests. This:

while [ date +"%T" -gt '06:00:00' && date +"%T" -lt '21:00:00']

is the same as

while test date +"%T" -gt '06:00:00' && date +"%T" -lt '21:00:00'

Let's go through a few things here:

  • date + %T is not a valid operator for the test command.

The test command can't execute a command internally. What you need to do is to put that command in $(..) and probably use quotes to play it safe. Thus:

while test "$(date +"%T")" -gt '06:00:00' && "$(date +"%T")" -lt '21:00:00'
  • The && is not a valid operator in the test command. What you probably want is -a which is the and conjunctive for stringing together two tests in test.

This would give you:

while test $(date +"%T") -gt '06:00:00' -a "$(date +"%T")" -lt '21:00:00'
  • There are two separate greater than test operators for comparisons. One is for strings and one is for integers. The -gt is the test for integers. Since you're dealing with strings, you need to use >:

    while test "$(date +"%T")" > '06:00:00' -a "$(date +"%T")" < '21:00:00'

As an alternative, you could have also used the && conjunctive instead of -a, but each side of the && would have to be separate test statements:

while test "$(date +"%T")" > '06:00:00' && test "$(date +"%T")" < '21:00:00'

Now, let's convert the test syntax back to [...] because it's easier on the eyes

while [ "$(date +"%T")" > '06:00:00' -a "$(date +"%T")" < '21:00:00' ]

OR

while [ "$(date +"%T")" > '06:00:00' ] && [ "$(date +"%T")" < '21:00:00' ]

By the way, the internal [[...]] is better -- especially with the > sign since it can be interpreted by the shell as a file redirect.

while [[ "$(date +"%T")" > '06:00:00' -a "$(date +"%T")" < '21:00:00' ]]

OR

while [[ "$(date +"%T")" > '06:00:00' ]] && [[ "$(date +"%T")" < '21:00:00' ]]