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
4
  • Related: unix.stackexchange.com/q/11530/117549
    – Jeff Schaller
    Commented Jan 26, 2018 at 11:09
  • 1
    Looks like you're mixing up Makefile variables and shell variables...
    – Kusalananda
    Commented Jan 26, 2018 at 11:10
  • @Kusalananda sorry but where did I use shell variables?
    – smarber
    Commented Jan 26, 2018 at 11:13
  • 1
    @smarber Exactly. Shell code can only change shell variables.
    – Kusalananda
    Commented Jan 26, 2018 at 11:15

1 Answer 1

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

10
  • 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. Commented Jan 26, 2018 at 11:28
  • @StéphaneChazelas True! fixed now thanks! Yeah [[ ... ]] must be bash I assume, and that's fine for me... Thanks again!
    – smarber
    Commented Jan 26, 2018 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 Commented Jan 26, 2018 at 11:59
  • 1
    @smarber, yep (see pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). Though you could just begin the script with c=$(COUNT) and you wouldn't need the default value
    – ilkkachu
    Commented Jan 26, 2018 at 12:27
  • 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. Commented Jan 26, 2018 at 17:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .