I'm new to the Ash shell so my apologies if this is well known. In normal maths and other shells and languages I've used, the modulo operator always returns a positive remainder. For example see this discussion (first post so I can't hyperlink it):
The Ash shell (and I am told Bash too) return a negative result. This code run on BusyBox illustrates the problem:
#!/bin/sh
echo "The result of A % B should be an integer in the range 0 to B-1"
echo "(9-7) % 5 = $(((9-7)%5)) (should be 2)."
echo "(7-9) % 5 = $(((7-9)%5)) (should be 3)."
exit 0
Is this accepted as just a 'feature' of the shells or is it a bug?
@Scrutinizer - what did you try, the original code? The '$((' '))' construct requires spaces around it I believe.
NO! it does not need spaces, just leading spaces
I'm wrong - the $(( does not require spaces it is part of command substitution (from opnegroup.org):
But since that is true then why do we get wacky (divergent) results with POSIX-compliant shells? my ksh did not like it. My bash (3.4.3) did after I added some spaces. It specifically complained about %5. Hmm.
Thanks Jim, I tried that and it made no difference:
echo "The result of A % B should be an integer in the range 0 to B-1, let's test it ..."
echo "(9-7) % 5 = $(( $(( 9 - 7 )) % 5 )) (should be 2)."
echo "(7-9) % 5 = $(( $(( 7 - 9 )) % 5 )) (should be 3)."
Output:
The result of A % B should be an integer in the range 0 to B-1, let's test it ...
(9-7) % 5 = 2 (should be 2).
(7-9) % 5 = -2 (should be 3).
No spaces are required in an arithmetic evaluation. Spaces are sometimes required when you are doing command substitution when the command being executed by the command substitution starts with a subshell because the $(( could be the start of an arithmetic evaluation or could be the start of a command substitution. If it is intended to be a command substitution starting with a subshell, you may need to have a space between the two opening parentheses to avoid an ambiguity in the shell's grammar.
The following:
echo "(9-7) % 5 = $(($((9-7))%5)) (should be 2)."
should always work, but:
echo "$((echo a;echo b)|(wc -l;echo done))"
might or might not work. To make it work reliably, you need a space as shown below:
var=$( (echo a;echo b)|(wc -l;echo done))
According to the standards, shell script writers should ALWAYS put a space between the parentheses when it is intended to be a command substitution starting with a subshell.
which covers this case and the result should be positive or "matching the divisor". As you point out, the Wikipedia table shows BASH gives the same sign as the dividend and ASH seems to be the same.
My application was for a circular counter: given a value m, in the range 0..(size-1), the next entry is next=$(( ($m+1) % $size)) while the previous should be prev=$(( ($m-1) % $size) but that gives -1 for $m=0. The workaround is to use prev=$(( ($m+$size-1) % $size) which should work on any shell.
Thanks for all the detailed replies, I've picked up quite a bit on the syntax from this thread as well as resolving the question.