0
#!/bin/sh

export $REG=0x000000
export $DONE=0x4A9FFF

while $REG -le $DONE
   do  
    (($REG+1))
    printf 'wm8280_reg get 0x'$REG'\r\n' 
    sleep 1 
   done

This leads to the errors:

./wm8280_reg_get1.sh: line 3: export: `=000000': not a valid identifier
./wm8280_reg_get1.sh: line 4: export: `=4A9FFF': not a valid identifier
./wm8280_reg_get1.sh: line 6: -le: command not found

I am pretty sure that dash uses -le for "Less than or Equal", but clearly I am wrong. In addition, I am not sure why my variables are wrong. What'm I doing wrong?

Wildcard
  • 36,499

3 Answers3

3

The standard (POSIX) sh syntax which should work in any conformant implementation including recent versions of dash would be:

#!/bin/sh -

reg=0x000000
done=0x4A9FFF

while [ "$((reg))" -le "$((done))" ]; do
  reg=$((reg + 1))
  printf 'wm8280_reg get 0x%06X\r\n' "$reg"
  sleep 1 
done

If you need to be portable to versions of dash prior to 0.5.5 (2009), you'll need:

#!/bin/sh -

reg=0x000000
done=0x4A9FFF

while [ "$(($reg))" -le "$(($done))" ]; do
  reg=$(($reg + 1))
  printf 'wm8280_reg get 0x%06X\r\n' "$reg"
  sleep 1 
done

The POSIX spec used not to be very clear on how $((reg)) should be expanded (it's still not very clear in the text, but at least the position of the Open Group on that has been clarified on the mailing list and the conformance test suite) and dash used to not support bare variables (without $) in its arithmetic expressions, while the specification for "$(($ver)) has always been a lot clearer.

Note that $reg will be decimal after the first invocation of reg=$(($reg + 1)) so you might as well convert the variables to decimal at the start. Also note that it will print numbers 0x1 to 0x4AA000 (56 days later); a more conventional (and legible IMO) way to write it would be:

#!/bin/sh -

reg=$((0x000001))  done=$((0x4AA000))

while [ "$reg" -le "$done" ]; do
  printf 'wm8280_reg get 0x%06X\r\n' "$reg"
  sleep 1 
  reg=$(($reg + 1))
done

Also, do not expect that to print a line exactly every second. There will be a drift incurred by the time spent running those commands.

Details:

  • export is to export variables to the environment of commands to be run. It doesn't make sense to use it here. Those variables are only used here to pass their value as argument to commands.
  • You put $ in front a variable when you want the value to be expanded. export $reg=0x000000 would run a export value-of-reg=0x000000.
  • while is followed by a command (or a list of commands) to execute whose exit status determines the loop condition. Here, we need the [ command (built in most shells) that can be used to perform various kinds of tests like those integer comparisons.
  • those arithmetic comparison operators of [ only support decimal integer constants though, so we use the shell arithmetic expansions (which support hexadecimal) to convert them to decimal.
  • cosmetic: by convention, we generally use all-uppercase variable names for environment variables or variables which should have a global scope in a complex script that uses functions for instance.
1

The example

#!/bin/sh

export $REG=0x000000
export $DONE=0x4A9FFF

while $REG -le $DONE
   do  
    (($REG+1))
    printf 'wm8280_reg get 0x'$REG'\r\n' 
    sleep 1 
   done

should change:

  • the two lines with export should not have $ prefixing the variables
  • use [ and ] around the $REG -le $DONE expression (or use test before)
  • quote variables when they are read, as in the expression.
  • use printf for formatting, not string-concatenation

So you would have something like

#!/bin/sh

export REG=0x000000
export DONE=0x4A9FFF

while [ "$REG" -le "$DONE" ]
   do  
    (($REG+1))
    printf 'wm8280_reg get 0x%s\r\n' "$REG" 
    sleep 1 
   done

This line

    while [ "$REG" -le "$DONE" ]

could also be written as

    while test "$REG" -le "$DONE"

(both are legal).

A quick read of dash's manual page does not indicate that it honors hexadecimal constants. You may have to change those exports to use decimal values, e.g.,

export REG=0
export DONE=489063

and the printf could then use %06X rather than %s, to print the values in hexadecimal.

Using cuonglm's suggestion, instead you could use

while [ "$((REG))" -le "$((DONE))" ]  

and

REG=$(($((REG))+1))

That refers to POSIX shell 2.6.4 Arithmetic Expansion:

Only the decimal-constant, octal-constant, and hexadecimal-constant constants specified in the ISO C standard, Section 6.4.4.1 are required to be recognized as constants.

However, testing dash on Debian 7 and 8, the increment-expression probably does not work as intended by OP, because the result is decimal, while the printf assumes it is hexadecimal. Also, REG=((REG+1)) does not work in that version of dash. (bash is a different story, of course).

Thomas Dickey
  • 76,765
  • Thank you for your help; I really appreciate it. I am a bit crap at scripting in general. – The Other NumberSix Jan 29 '16 at 01:27
  • It is now a lot less broken than before :)

    It is running, but not incrementing, so I will continue to plug away at it.

    – The Other NumberSix Jan 29 '16 at 01:45
  • You can always use echo $((0x<hex_string>)) to convert hex string to decimal in POSIX shell. So, just [ "$((REG))" -le "$((DONE))" ]. – cuonglm Jan 29 '16 at 01:46
  • I went with

    ((REG=REG+1))

    And it seems to have done it. Thanks for all your help. I owe you a beer.

    – The Other NumberSix Jan 29 '16 at 01:59
  • @cuonglm, x=$((x+1)), : "$((x+=1))", x=$(($((x))+1)), x=$(($x+1)) are all POSIX as long as $x contains a valid integer constant (decimal, octal or hexa), but some old versions of dash only supported the latter as earlier versions of the POSIX spec were unclear about it (still not very clear but a bit better), : "$((x++))" is not POSIX though allowed (as in $((x++-1)) can be either $(((x++) - 1)) or $((x + +(-1))) – Stéphane Chazelas Feb 01 '16 at 20:54
  • There's not shell where REG=((REG+1)) works. ITYM ((REG = REG + 1)) (or ((REG++)) in those shells that have that non-standard ((...)) construct). – Stéphane Chazelas Feb 01 '16 at 20:59
  • I'm not sure I understand your answer. (($REG+1)) is not standard sh syntax, and even in shells that have ((...)), that wouldn't make much sense. The arithmetic operators of the [ command only recognise decimal numbers, not hexadecimal (even in bash or ksh or zsh). – Stéphane Chazelas Feb 01 '16 at 21:24
  • @StéphaneChazelas: Anyway, your comments (or answers) were always useful. Another question, is using $((x)) always safe to convert x to integer. sh -c 'echo "$(($1))"' _ "-0xA" worked in all POSIX shells I tried. Heirloom sh, schily osh and schily sh all printed the string as-is. – cuonglm Feb 02 '16 at 02:01
  • 1
    @cuonglm $((...)) is a POSIX thing (coming from ksh), it's not Bourne. Yes, like I said, $((x)) is POSIX as long as x contains a valid integer constant (decimal, octal or hexadecimal), but didn't work in dash prior to 0.5.5. $(($x)) is also POSIX and works in all shells. Watch out for things like $((-$x)) that don't work properly for negative values if $x in shells that support the -- operator. – Stéphane Chazelas Feb 02 '16 at 09:28
0

Right, Thank you all for your input. The final script looks like:

#!/bin/sh

export REG=0
export DONE=4890632

while [ "$((REG))" -le "$((DONE))" ]
   do  
    wm8280_reg get $REG >> "wm_reg_dump.txt"
    REG=$(($((REG))+1))
   done

This runs in DASH and has the required output. Sure, the code takes 8 hours to run, but hey, set it and forget it :)

  • REG=$(($((REG))+1)) is not different from REG=$((REG + 1)). If you need to support very old versions of dash, it's REG=$(($REG + 1)) you need. – Stéphane Chazelas Feb 01 '16 at 20:58