2

I need to perform an arithmetic operation inside a bash loop as explained below

CYCLE?=3
COUNT=1

download_when_ready: ## Will try the download operations many times till it succeeds or it reaches 10 tries
    while ! composer update $(bundle) 2> /dev/null && [[ $(COUNT) -lt 10 ]]; \
    do \
        COUNT=$$(( $(COUNT)+1 )); \
        SLEEP=$$(( ($(COUNT) / $(CYCLE)) + ($(COUNT) % $(CYCLE)) )); \
        echo "count $(COUNT)"; \
        echo "cycle $(CYCLE)"; \
        echo "sleep $(SLEEP)"; \
        sleep $(SLEEP); \
    done

This never stops and gives the following:

count 0
cycle 4
sleep 0

count 0
cycle 4
sleep 0

....

count 0
cycle 4
sleep 0

As you can see, variables have the initial values and never change !

UPDATE

PRETTY_NAME="SUSE Linux Enterprise Server 11 SP4"

However the following code keeps the value of $$c empty, before the while loop and inside it.

CYCLE?=3
COUNT=1

download_when_ready: ## Will try the download operations many times till it succeeds or it reaches 10 tries
    @c=$(COUNT);
    @echo $$c;
    while ! composer update $(bundle) 2> /dev/null && [[ $(COUNT) -lt 10 ]]; \
    do \
        echo "$$c"; \
    done
smarber
  • 1,191
  • 2
  • 12
  • 25

1 Answers1

5

UPDATE

Thanks to @Kusalananda comment, I figured it out.

I used variable as initial values for variables

CYCLE?=3
COUNT=1

download_when_ready: ## Will try the download operations many times till it succeeds or it reaches 10 tries
    while ! composer update $(bundle) 2> /dev/null && [ "$$c" -lt 10 ]; \
    do \
        c=$$(( $${c:-$(COUNT)}+1 )); \
        s=$$(( ($$c / $(CYCLE)) + ($$c % $(CYCLE)) )); \
        echo "count $$c"; \
        echo "cycle $(CYCLE)"; \
        echo "sleep $$s"; \
        sleep $$s; \
    done

And this does work!

count 1
cycle 4
sleep 1
count 2
cycle 4
sleep 2
count 3
cycle 4
sleep 3
count 4

Thanks to @Kusalananda & @Stéphane Chazelas

smarber
  • 1,191
  • 2
  • 12
  • 25
  • 1
    [[ $(COUNT) -lt 10 ]] will be expanded by make to [[ 1 -lt 10 ]] before calling sh. So that tell will always be true. Also note that [[...]] is not sh syntax. – Stéphane Chazelas Jan 26 '18 at 11:28
  • @StéphaneChazelas True! fixed now thanks! Yeah [[ ... ]] must be bash I assume, and that's fine for me... Thanks again! – smarber Jan 26 '18 at 11:35
  • 2
    [[...]] is ksh syntax, also supported by bash and zsh. It's not in sh. If you want to use that, you should set the SHELL make variable (which is independant from the SHELL environment variable) to a shell that supports that non-standard syntax. But note that not all systems have bash installed and those that do don't have it always in the same place (can be /usr/gnu/bin/bash, /usr/local/bin/ash, /opt/gnu/bin/bash... depending on the system). Just use [ "$$c" -lt 10 ] and you won't have to require bash/ksh/zsh – Stéphane Chazelas Jan 26 '18 at 11:59
  • @StéphaneChazelas I prefer this syntax [ $${c:-0} -lt 10 ], is it standard ? – smarber Jan 26 '18 at 12:23
  • 1
    @smarber, yep (see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 ). Though you could just begin the script with c=$(COUNT) and you wouldn't need the default value – ilkkachu Jan 26 '18 at 12:27
  • @smarter, yes, that's standard but that's still not what you want to do, that would still potentially call [ with a value of $c that is taken from the environment. It's very dangerous to use uninitialised shell variables in arithmetic contexts. Here, you'd want to initiallise the c shell variable from the COUNT make variable before ever expanding/using it: c=$(COUNT); while ... – Stéphane Chazelas Jan 26 '18 at 12:37
  • I don't know why but with c=$(COUNT); while ... the variable c does not get set with $(COUNT) value... @StéphaneChazelas – smarber Jan 26 '18 at 13:29
  • Works for me with a rule that does c=$(COUNT); echo $$c – Stéphane Chazelas Jan 26 '18 at 16:35
  • @StéphaneChazelas it does not work for me, I updated my question – smarber Jan 26 '18 at 17:06
  • 2
    You need the backslash at the end of c=$(COUNT). Otherwise make runs two separate sh instances. That's why you have ; at the end of your line. That's because those lines end up joined together because of the backslash. – Stéphane Chazelas Jan 26 '18 at 17:22