Skip to content

Commit

Permalink
Concept: Arithmetic
Browse files Browse the repository at this point in the history
  • Loading branch information
glennj committed Feb 1, 2025
1 parent 26451de commit 1ae6bb4
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 39 deletions.
5 changes: 5 additions & 0 deletions concepts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,8 @@ x. `set` command and "strict mode"
- `-opt` versus `+opt`
- debugging with `-x` and `-v`
- shellcheck
- https://mywiki.wooledge.org/BashFAQ/105
- http://redsymbol.net/articles/unofficial-bash-strict-mode/
- https://www.shellcheck.net
- https://www.shellcheck.net/wiki/
144 changes: 105 additions & 39 deletions concepts/arithmetic/introduction.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,143 @@
# Introduction to Arithmetic in the Bash Shell

Bash can do arithmetic but _only with integers_.
(If you need to do floating-point math, you'll need to call out to some external program.)
If you need to do floating-point math, you'll need to call out to some external program (like [awk][awk] or [bc][bc]).

The arithmetic operators are listed (in order of precedence) in the manual: [Shell Arithmetic][arithmetic].

Additionally, Bash only performs arithmetic in certain places (known as arithmetic "contexts").

## Arithmetic Expansion

## Arithmetic Conditional
Arithmetic expansion looks like parameter expansion, but with double parentheses.

```bash
$((expression))
```

This expansion returns the result of evaluating the expression.
It is common to capture the result in a variable:

```bash
x=$((3 + 4))
```

## Other places with arithmetic "context"
## Arithmetic Conditional

- array index
- ${var:offset:length}
- `let` command
- `declare -i` and variable assignment
The arithmetic conditional is performed within double parentheses _without_ the leading `$`:

```bash
((expression))
```

This does not return the result.
However it does produce an _exit status_.

~~~~exercism/caution
Assigning variables in a conditional construct
* The exit status is 0 (success) if the expression's result is **non-zero**.
* The exit status is 1 (failure) if the expression's result is **zero**.

beware of `set -e`
~~~~exercism/note
Bash uses the same sense of true/false as the C language: zero is false, non-zero is true.
~~~~

<!-- ------------------------------------------------------------ -->
This syntax is typically used in two places:

1. as the COMMAND part of an [`if` statement][conditional-if] or a [`while` loop][looping-while].

```bash
if ((x == y - 1)); then ...
while ((y > 0)); do ...
```
1. as a standalone command to modify a variable's value.
```bash
((++x))
echo "new value for x is $x"
```
There are two constructs that you'll use for arithmetic:
~~~~exercism/caution
Be careful using arithmetic this way combined with `set -e` that the expression does not result in zero.
The exit status will be non-zero.
1. `$((...))` -- a construct that evaluates the given expression and outputs the answer
1. `((...))` -- a construct that evaluates the given expression and returns a true/false value
```bash
x=0
((x++))
echo "you will see this"
Whether to use one construct or the other depends on what you want to do with the answer:
set -e
x=0
((x++))
echo "shell exits on the previous line; you won't see this"
```
~~~~
* If you need to answer for further use, then use `$((...))`.
This is called "arithmeticjk expansion".
## Other Places With Arithmetic Context
There are a few other places that are arithmetic contexts:
* The [arithmetic for-loop][looping-for].
* The index part of a numerically-indexed array.
```bash
height=10
width=20
area=$((height * width))
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
```
* If you need to know if an arithmetic _comparison_ is true, then use `((...))`.
This is called the "arithmetic conditional" construct.
* The offset and length parts of the `${var:offset:length}` expansion.
* The [`let` builtin command][let].
* Note that `((expression))` is preferred over `let "expresson"`.
Unlike the arithmetic conditional construct, the `let` expression is subject to filename-expansion and word-splitting, and it requires quoting.
* Variable assignment, when the variable has the "integer attribute".
```bash
height=10
width=20
if ((height > width)); then
echo "Taller"
else
echo "Wider"
fi
# variable x does not have the integer attribute
x="3 + 4"
declare -p x # => declare -- x="3 + 4"
# variable y has the integer attribute
declare -i y
y="4 + 5"
declare -p y # => declare -i y="9"
```
~~~~exercism/note
~~~~exercism/note
It is the author's opinion that `declare -i` has limited usage.
It's main benefit is to allow "bare" arithmetic (without the arithmetic expression syntax).
This can cause confusion for readers of your code, where arithmetic expressions are evaluated in unexpected places.
~~~~
## Using Variables in an Arithmetic Expressions
There is one aspect of Bash arithmetic that improves readability.
```bash
height=10
width=20
area=$((height * width))
```
Did you see that the `$` dollar signs are missing from the variables in the arithmetic expression?
Bash lets you do that.
It makes expressions much easier to read.
The manual says it this way:
> Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax.
It even works for array elements:
```bash
point1=(10 15) # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))
```
~~~~

## Arithmetic Boolean Values

Bash uses the same sense of true/false as the C language:

* the number 0 is false.
* any other number is true.

[arithmetic]: https://www.gnu.org/software/bash/manual/bash.html#Shell-Arithmetic
[conditional-if]: https://exercism.org/tracks/bash/concepts/conditionals#h-the-if-command
[looping-while]: https://exercism.org/tracks/bash/concepts/looping#h-while-loops
[looping-for]: https://exercism.org/tracks/bash/concepts/looping#h-arithmetic-for-loop
[let]: https://www.gnu.org/software/bash/manual/bash.html#index-let
[awk]: https://www.gnu.org/software/gawk/manual/html_node/index.html
[bc]: https://www.gnu.org/software/bc/manual/html_mono/bc.html

0 comments on commit 1ae6bb4

Please sign in to comment.