3

I want to assign a variable based upon the modulo of another variable (subject number, in this case), getting a number (representing subject condition) which cycles over N conditions.

In other words, I want something very similar to the modular function a%n, but for full multiples of n I want to return n, rather than 0.

I think the easiest way to do this is with an or conditional, such that if the mod function returns 0, N will be returned instead, but I can't get the syntax to work.

subj=4
list=$((subj%4)) || 4
echo $list

just returns the result of subj%4, and ignores the conditional, while replacing || with -o fails.

If I try and use brackets around the whole thing, I just get a binary output of the conditional test.

What am I missing?

Henry Brice
  • 280
  • 1
  • 2
  • 18

3 Answers3

3

Yes, your idea is right, you just put the block inside construct used for running C-style arithmetic operations i.e. use the Arithmetic expressions operator

((list = n%4==0 ? 4 : 0 ))

Using a variable and a function separately as OP had asked for

modulo() {
    local n="${1?Needs an argument}"
    echo $((n%4==0 ? 4 : 0 ))
}

Modify this function to return custom values as you need.


modulo 5
0    
modulo 4
4    
modulo 8
4
modulo
bash: 1: Needs an argument
Inian
  • 80,270
  • 14
  • 142
  • 161
  • Thanks, that's great! What I was actually looking for is `(( list = n%4==0 ? 4 : subj%4 ))`, but your answer got me there :) Do you have a source for this `? :` syntax? I cannot find one anywhere, and for obvious reasons it's difficult to search for.. – Henry Brice Jan 29 '18 at 10:54
  • 1
    @HenryBrice: It is a standard C style ternary operator. You can use them in `bash` under `$((..))` – Inian Jan 29 '18 at 10:55
1

In bash, your || means: "If $((subj%4)) returns non-zero, do 4".

It will not work, at least because $((subj%4)) returns zero (exit code, i mean).

In your case, do something like that:

subj=4
list=$((subj%4))
if [ "${list}" -eq 0 ]
then
    echo "4"
else
    echo "${list}"
fi
Viktor Khilin
  • 1,760
  • 9
  • 21
  • 1
    @Aserre i'm not. `||` means that right part of the code will be executed if only it's left part finished with non-zero return code. In OP's situation, `$?` is `0` every time. So, right part of his code will never be executed. Edited my post a bit, may be it was written not clearly for understanding. – Viktor Khilin Jan 29 '18 at 10:14
  • Isn't it the opposite? If the right hand returns zero then the left hand should be evaluated? Is there not a less clunky way to this, than having a whole `if` block? It feels like there should be some way to one-line this? – Henry Brice Jan 29 '18 at 10:45
  • 1
    @HenryBrice for exit codes in Unix, 0 is a success, 1 or another value is a failure. – Aserre Jan 29 '18 at 10:48
  • Ahhh... that explains a lot! Thanks :) As you can tell, I'm something of a noob to bash scripting... – Henry Brice Jan 29 '18 at 10:57
1

Here is a version without branching:

$((  1 + (n + m - 1)  % m ))

I doubt that branching is an issue in a bash script but that could be useful in other cases.

Example:

cycle() { 
    n=$1; m=$2; 
    $(( 1 + (n + m - 1)  % m )) 
} 

for ((i=0; i<10; i++)); do echo $i: $(cycle $i 4); done

Output:

0: 4
1: 1
2: 2
3: 3
4: 4
5: 1
6: 2
7: 3
8: 4
9: 1
olivecoder
  • 2,858
  • 23
  • 22