Eric
Eric

Reputation: 3040

How to use mod operator in bash?

I'm trying a line like this:

for i in {1..600}; do wget http://example.com/search/link $i % 5; done;

What I'm trying to get as output is:

wget http://example.com/search/link0
wget http://example.com/search/link1
wget http://example.com/search/link2
wget http://example.com/search/link3
wget http://example.com/search/link4
wget http://example.com/search/link0

But what I'm actually getting is just:

    wget http://example.com/search/link

Upvotes: 238

Views: 402988

Answers (6)

Gabriel Staples
Gabriel Staples

Reputation: 53065

Math in bash: how to use all bash operators (+, -, /, *, **, &, &&, <<, etc.), and arithmetic expansion, in bash

Of the 346k visitors to this question thus far, I'd be willing to bet 344.9k of them just want the title of this question answered 😃:

How to use mod operator in bash?

Even I googled "bash modulus" looking for that answer, and landed here. So, now that I've figured it out, let's just jump straight to it:

How to use the modulus (%) operator in bash

Basic example: just do this, for instance:

# 7 mod 4 (answer is 3, but to print the output you must use one of the cmds
# below)
$((7 % 4))

# [PREFERRED: no quotes]
# print the result (double quotes are not required)
echo $((7 % 4))

# print the result (with double quotes if you like)
echo "$((7 % 4))"

Example with variables:

num1="7"
num2="4"

# [PREFERRED: no $ signs nor extraneous quotes] result is 3
echo $((num1 % num2))

# Also ok: with $ signs
echo $(($num1 % $num2))

# Also ok: with $ signs and extra quotes
echo "$(("$num1" % "$num2"))"

Example with executable calls, such as nproc, which returns the number of logical processors on your machine:

# Get the number of logical processors on your machine, and add 1 to it
echo $(( $(nproc) + 1 ))

Store the result into a variable:

mod=$((num1 % num2))
echo "$mod"  # result is 3

num_cores_plus1=$(( $(nproc) + 1 ))
echo "$num_cores_plus1"  # result for me is 21

The main links to study for these concepts are these, from the official GNU bash user manual:

  1. Bash Arithmetic Expansion
  2. Bash Shell Arithmetic

More on bash "arithmetic expansion"

I learned the above from @Mark Longair's answer (although it took me some effort to comprehend it all), and that's where I got the link just below. I then did more research.

The $(( )) part is called "Arithmetic Expansion", and is described in the official GNU bash user manual here: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Arithmetic-Expansion.

Basic examples (place echo in front of each one to see the result print to the screen):

# general form
$((mathematical_expression))

# addition
$((7 + 4))  # 11

# subtraction
$((7 - 4))  # 3

# modulus (remainder)
$((7 % 4))  # 3

# logical AND
$((7 && 4))  # 1

# bitwise AND
$((7 & 4))  # 4

# etc.
# See the full operator list below for more

Double quotes around the arithmetic expansion are not needed. From the manual above (emphasis added):

The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter and variable expansion, command substitution, and quote removal. The result is treated as the arithmetic expression to be evaluated. Arithmetic expansions may be nested.

For all shell arithmetic operators, see the "Shell Arithmetic" section of the GNU bash manual here: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Arithmetic

You essentially have all of the C language mathematical operators at your disposal. The arithmetic is done "in fixed-width integers with no check for overflow", so if you do echo $((11/10)) or echo $((19/10)) you'll get 1 in both cases since the fractional part is truncated for integers.

From the manual link just above (emphasis added):

Evaluation is done in fixed-width integers with no check for overflow, though division by 0 is trapped and flagged as an error. The operators and their precedence, associativity, and values are the same as in the C language.

Since the arithmetic operators in bash have the same precedence as in C, as it states above, you can also reference the C Operator Precedence from the cppreference community wiki here: https://en.cppreference.com/w/c/language/operator_precedence <-- put that in your toolbag.

Shell Arithmetic: here are all of the supported operators from the GNU Bash manual

They are listed in order of highest to lowest precedence:

  1. id++ id--
    1. variable post-increment and post-decrement
  2. ++id --id
    1. variable pre-increment and pre-decrement
  3. - +
    1. unary minus and plus
  4. ! ~
    1. logical and bitwise negation
  5. **
    1. exponentiation
  6. * / %
    1. multiplication, division, remainder
  7. + -
    1. addition, subtraction
  8. << >>
    1. left and right bitwise shifts
  9. <= >= < >
    1. comparison
  10. == !=
    1. equality and inequality
  11. &
    1. bitwise AND
  12. ^
    1. bitwise exclusive OR
  13. |
    1. bitwise OR
  14. &&
    1. logical AND
  15. ||
    1. logical OR
  16. expr ? expr : expr
    1. conditional operator
  17. = *= /= %= += -= <<= >>= &= ^= |=
    1. assignment
  18. expr1 , expr2
    1. comma

Using alternate bases in your arithmetic, such as binary (base-2), octal (base-8), and hex (base-16)

To learn about using different bases, such as base-2 (binary), base-8 (octal) or base-16 (hex) with the bash arithmetic operators, read the next couple paragraphs below the "Shell Arithmetic" list above in the manual.

Here are a few quick examples with input numbers which are decimal (base-10), octal (base-8), hex (base-16), and binary (base-2), used in the math:

# hex 0xa (decimal 10) + decimal 5 = decimal 15
echo $((0xa + 5))  # prints `15` (decimal 15)
# OR (same thing)
echo $((16#a + 5))  # prints `15` (decimal 15)

# octal 071 (decimal 57) + hex 0xaa (decimal 170) = decimal 227
echo $((071 + 0xaa))  # prints `227` (decimal 227)
# OR (same thing)
echo $((8#71 + 16#aa))  # prints `227` (decimal 227)

# binary 1011 (decimal 11) + decimal 2 = decimal 13
echo $((2#1011 + 2))  # prints `13` (decimal 13)

# binary 1111 (decimal 15) + binary 11111 (decimal 31) = decimal 46
echo $((2#1111 + 2#11111))  # prints `46` (decimal 46)

To print as hex, use printf "0x%X\n" number:

# prints `0x2E` (hex 2E, or decimal 46)
printf "0x%X\n" $((2#1111 + 2#11111))

To print as binary, use bc (see my answer here):

# prints `0b101110` (decimal 46)
printf "0b%s\n" "$(echo "obase=2; $((2#1111 + 2#11111))" | bc)"

See also

  1. [my answer] How do I use floating-point arithmetic in bash?
  2. If you need a full floating point library in bash, that you can easily import (source), use my code here: bash/floating_point_math.sh in my eRCaGuy_hello_world repo.
    1. See also my answer about using Bash libraries here: Detailed example: how do you write, import, use, and test libraries in Bash?

Upvotes: 4

Glenn J. Schworak
Glenn J. Schworak

Reputation: 340

This post is rather old but I thought I would contribute since I stumbled upon it while trying to research the same issue of setting keyboard color through automation.

I created a simple BASH script that I call from my ROOT chrontab every minute to set the keyboard color as the day progresses. You can tweak the color patterns and the modulo to match your needs. This is just a good starting point.

#!/bin/bash
# must run as ROOT to work
# put in your root crontab to change the color at set times

sec=$(date +%s)
min=$(( $sec / 60 ))
col=$(( $min % 7 ))
colors=('0000FF' '00FF00' '00FFFF' 'FF0000' 'FF00FF' 'FFFF00' 'FFFFFF')
colorFile="/sys/class/leds/system76_acpi::kbd_backlight/color"

if [ -f "$colorFile" ]; then
    echo "Set keyboard to color $col ~ ${colors[$col]}"
    echo "${colors[$col]}" > "$colorFile"
fi

Hope you like it.

Upvotes: 0

Higor E.
Higor E.

Reputation: 716

You must put your mathematical expressions inside $(( )).

One-liner:

for i in {1..600}; do wget http://example.com/search/link$(($i % 5)); done;

Multiple lines:

for i in {1..600}; do
    wget http://example.com/search/link$(($i % 5))
done

Upvotes: 33

h__
h__

Reputation: 883

This might be off-topic. But for the wget in for loop, you can certainly do

curl -O http://example.com/search/link[1-600]

Upvotes: 15

Mark Longair
Mark Longair

Reputation: 467921

Try the following:

 for i in {1..600}; do echo wget http://example.com/search/link$(($i % 5)); done

The $(( )) syntax does an arithmetic evaluation of the contents.

Upvotes: 331

Chris Eberle
Chris Eberle

Reputation: 48795

for i in {1..600}
do
    n=$(($i%5))
    wget http://example.com/search/link$n
done

Upvotes: 56

Related Questions