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,181
  • 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,181
  • 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