0

In a bash script (run every 30min), I want to do an action every even hour (and minutes 0), so I want to test the hour number if it's an even number or not. So I use this:

        HEURE=$(date +"%H")
        MINUTES=$(date +"%M")
        HEURE_PAIRE=""
        MINUTES_ZERO=""
        [[ $((HEURE % 2)) -eq 0 ]] && HEURE_PAIRE="OUI" || HEURE_PAIRE="NON"
        [[ $MINUTES -eq 0 ]] && MINUTES_ZERO="OUI" || MINUTES_ZERO="NON"
        if [[ "${HEURE_PAIRE}" == "OUI" ]] && [[ "MINUTES_ZERO" == "OUI" ]]; then
            # My code
        fi

But I get an error for this line (n°170 in my script) [[ $((HEURE % 2)) -eq 0 ]] && HEURE_PAIRE="OUI" || HEURE_PAIRE="NON" when it was 08:00, 08:30, 09:00, 09:30, but not for 10:00 or 10:30. See the error I get:

/volume4/docker/_Scripts-DOCKER/driver-pkgctl-r8152-restart-reload.sh: line 170: 08: value too great for base (error token is "07")
/volume4/docker/_Scripts-DOCKER/driver-pkgctl-r8152-restart-reload.sh: line 170: 09: value too great for base (error token is "09")

I expected to have it working like this: For 08:00, the 08%2 should return 0 as a result, proving hour is even. For 09:00, the 09%2 should return 1 as a result, proving hour isn't even. Not having an error

I assume it's the 0 before the 8 or 9... But I don't know how to fix this.

I found a workaround with this question by adding this:

HEURE=${HEURE#0}

It seems to be working.

My question: is there a better way to achieve what I want?

Thanks in advance.

5 Answers 5

0

Corrected script:

forcing base 10¹:

#!/bin/bash

heure=10#$(date +"%H") minutes=10#$(date +"%M")
if (( heure % 2 == 0 && minutes == 0 )); then
    # My code
fi

Another way using POSIX parameter expansions

#!/bin/bash

heure=$(date +"%H") minutes=$(date +"%M")
if (( ${heure#0} % 2 == 0 && ${minutes#0} == 0 )); then
    # My code
fi

another way using crontab

*/30 * * * * /path/to/script

Don't use UPPER case variables


((...)) and $((...)) are arithmetic commands, which returns an exit status of 0 if the expression is nonzero, or 1 if the expression is zero. Also used as a synonym for "let", if side effects (assignments) are needed. See http://mywiki.wooledge.org/ArithmeticExpression


¹ In arithmetic operations with bash, is there's a leading 0 like 08, it's treated as an octal number. To force base10, I use 10# prefix on hour and minutes


No need to quote in [[ ... ]] syntax, see http://mywiki.wooledge.org/BashFAQ/031 and http://mywiki.wooledge.org/BashGuide/TestsAndConditionals

Sign up to request clarification or add additional context in comments.

12 Comments

Thanks for your help. I learned something today :) May I submit the whole script to review?
Don't know what kind of review do you mean
@EdMorton # has to be the first character of a word to signify a comment. See rules 7, 8 and 9: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
@EdMorton I use a sort of 'cast' to avoid 'bash' to see e.x. 08 as octal. 10#08 is 'casted' in base 10
|
0

Setting the base is a good method for bash.

For POSIX shells that don't support that, there are other options.

Prefix with a non-zero digit:

heure=$(date +1%H)
...

Strip the leading zero:

heure=$(date +%H)
heure=${heure#0}
...

Compare textually:

heure=$(date +%H)
...
case $heure in ?[02468]) ... ;; esac
...

5 Comments

Your second proposition is the one I used before @gilles-quénot give the first answer. Does your second proposition works for 13h, 14h... 23h ? I live in France where time is on 24 hours, not 12h AM/PM.
Simple way to find out is to try it: eg. h=02; echo ${h#0}; h=14; echo ${h#0}
it works. And what about the "Compare textually" method?
what happens when you try it?
I don’t take time to try it, not yet. But I always try to understand the command before using it. And here si don’t see what would happen with a 2 numbers hour like 13 ou 23…
0

First this is bad:

HEURE=$(date +"%H")
MINUTES=$(date +"%M")

The time can switch e.g. from 10:59 to 11:00 in between. And then you get the wrong 10:00.

Use either

time=$( date +%H:%M )
HEURE="${time%:*}"
MINUTES="${time#*:}"

... or ...

time=$( date +%s )
HEURE=$( date -d@$time +%H )
MINUTES=$( date -d@$time +%M )

In both variants you get the time only once. In the second example you'll get the epoche-time and then calculates hour an minute from this time. Thew first example seems to be more efficient.

Then replace [[ $((HEURE % 2)) -eq 0 ]] by (( !( 10#$HEURE % 2 ))) or (( ( 10#$HEURE % 2 ) == 0 )). The prefix 10# forces the evaluation as decimal number.

Last note: In general, use lower case letters or mixed case for local var names.

1 Comment

That’s a good point. Time can change a little , yes. But my script is launched by DSM (Synology Nas) at 00:00 and every 30 minutes after that. S I assume that it fille be launched at 00:00:00 or some (milli)seconds after. And that’s not a problem in the tests made here . But you have a point here . It is preferable to get hour and minutes at the same time . I’ll implement that . Thanks.
0

An alternative method without using arithmetic expressions:

#!/bin/bash

hm=$(date +%H%M)
if [[ $hm = ?[02468]00 ]]; then
    # do stuff
fi

or, in POSIX shell:

#!/bin/sh

hm=$(date +%H%M)
case $hm in
    ?[02468]00) do_stuff ;;
esac

or, with Bash arithmetic in a single operation:

#!/bin/bash

if (( 10#$(date +%H%M) % 200 == 0 )); then
        echo "It is time" 
fi

2 Comments

Your solutions are similar to those of @jhnc . I like the one for bash. It seems efficient. I'm looking for the most efficient way to do what I want, and in a way I can understand :)
@m-nejat-aydin : Third method is working. Can you explain, please?
0

In numeric context, strings starting with a zero are considered octal. Therefore, 08 is in error.

However, I wouldn't do artithmetic to test for your condition. Instead, I would do something like

if [[ $(date +%HM) == ?[02468]00 ]]
then
  ....
fi

2 Comments

That’s what @m-nejat-aydin answered before you. Is it like regex but for bash? Would you explain please how it work?
It's not a regex, but a glob-pattern. In [[ E1 == E2 ]], E2 is treated as glob pattern and E1 is matched against it. Globbing syntax is explained in the section Pathname Expansion in the bash man-page.Be careful: If E2 is quoted, pattern matching is disabled. You can also use regex matching (bash uses POSIX extended regular expression). In this case the syntax is [[ E1 =~ E2 ]].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.