We’ve opened a new **DevOps (Bash, LAMP and LEMP Stack)
Professional position**. Part-time and entirely remote, of
course.

# Bash: let Statement vs. Assignment

Last updated: November 26, 2022

## 1. Overview

The built-in *let* command is part of the Bash and Korn shell fundamental software. Its main usage focuses on building variable assignments with basic arithmetic operations.

In this article, we describe the typical usage syntax of the command. Thereupon, we show all meaningful properties that differentiate it from the ordinary variable assignment case. Next, we proceed with a comparison with alternative commands performing similar variable assignments and mathematical operations. Along with every examined subject, in order to provide a solid understanding of the command, we highlight the compelling characteristics of the command through appropriate examples.

## 2. Syntax and Characteristics

The *let* command performs **variable assignments combined with basic mathematical evaluations**. Its typical syntax is quite straightforward in its simplest form:

`let statement1 [statement2 [...] ]`

where *statement1 [statement2 […]],* represent variable assignments to a number or a numeric evaluation through some mathematical operation.

**Variables involved in the statements obtain local scope** to the command, and their attributed values can only be fixed-width decimal integers. Any attempt to assign strings on variables leads to failure.

At last, there is a significant list of compelling properties that outline the outstanding flexibility and effectiveness of this command. A comparison against the common variable assignment operation will help us understand its potential. In the following sections, we are going to explore the basic properties through some comprehensive examples.

### 2.1. Multiple Statements and Variable Expansion

In a single command, multiple statements can **handily concatenate without the need for the semicolon punctuation ‘;’**. Normally its presence in a standard assignment expression distinguishes consequent commands staying in a single row. We can easily test this fact in the following example:

`$ let age=25 weight=57`

Moreover, an enhanced feature regarding the parameter expansion is that **the variables’ expansion may also be referenced simply by name without making use of the familiar prefix symbol ‘$’**. Let’s see how this applies in the following case:

```
$ let length=5 width=7 area=length*width
$ echo "length: $length - width: $width - area: $area"
length: 5 - width: 7 - area: 35
```

Finally, the drop of the prefix ‘$’ is not a restricted rule. This means that prefixed variables can still exist alone or mixed with the former type. In such a case, the presence of the variables’ expansion prefix works without conflicts:

```
$ let "x=3" "y=4" "z2=x**2+$y**2"; echo $x, $y, $z2
3, 4, 25
```

### 2.2. Variables and Operations in Assignments Using Double Quotes

Additional interesting capabilities for the *let* command come up when assignment definitions are surrounded by double quotes.

Under a normal variable assignment instance, no spaces can exist. For both the assignment operator ‘=’ and any binary operator, it is mandatory to keep a structure with operator and operands syntactically attached. While for typical variable declarations any in-between spare spaces break the assignment:

```
$ year= 1981
bash: 1981: command not found...
$ volume =125
bash: volume: command not found...
```

the *let* command succeeds with double quotes surrounding the expressions. In this case:

```
$ let "year= 1981" "volume =5**3"
$ echo "year: $year - volume: $volume"
year: 1981 - volume: 125
```

As an alternative solution,** multiple expressions may merge at a single double-quoted entity with subsequent statements separated with a comma ‘,’**. The latter example would appear under this prescription as follows:

`$ let "year= 1981 , volume =5**3"`

### 2.3. Scope Visibility

Let’s see what* are *the characteristics of the* let* command’s proper environment. ** The command builds a local scope for the variables acting in it.** Similarly to any other local character for variables, the presence of predefined variables in this environment results in overwriting:

```
$ version=10 let "version=11, release = version"; echo "$version, $release"
11, 11
```

Furthermore, an interesting property emerges when variables are set in their local scope, but their effective value is not yet reckoned. In such a case, the value does not update when we evaluate the variable. Let’s see the details from this case in the following example:

```
$ let "count=11, max=count++"; echo $count,$max
12,11
```

Above, the *count* variable’s increment at the moment of the *max* assignment is still pending. So, the proper value remains still intact. By requiring the evaluation of *count* update before the assignment on *max*, the update applies in the current shell. Finally, this also does for the *max* variable:

```
$ let "count=11, max=++count"; echo $count,$max
12,12
```

### 2.4. Exit Status With *let* Command

**The exit status at the end of a let command is determined by the value assigned to the rightmost variable.**

Independently of the assigned value of any previous statement, the last expression is the one that defines the returned exit status. More explicitly, in the last statement, if the variable’s value corresponds to 0, the exit status returned is 1 (FALSE). In any other case, with either a positive or negative value assigned, the exit status returned is 0 (TRUE).

Below, we can observe with a simple example how the exit status acts in each case. The retrieval of the exit status evaluation comes through the variable *$?* :

```
$ let "b=-1, k=0"; echo $?
1
$ let "b=0, k=0"; echo $?
1
$ let "b=0, k=1"; echo $?
0
```

### 2.5. Mathematical Operations

There is a large number of mathematical operations allowed within the assignment statements in a *let* command construct. Below, we list the operators with decreasing order of precedence significance. Operators residing in the same row grant the same level of precedence.

The precedence order for mathematical operators is determined by the following list (highest precedence at the top, lowest precedence at the bottom):

- Post-increment, post-decrement (
*id++*,*id–*) -
Pre-increment, pre-decrement (
*++id*,*–id*) -
Unary minus, unary plus (
*–*,*+*) -
Logical negation, bitwise negation (
*!*,*~*) -
Exponentiation (
****) -
Multiplication, division, the remainder (modulo) (
***,*/*,*%*) -
Addition, subtraction (
*+*,*–*) -
Bitwise shifts (
*<<*,*>>*) -
Numeric comparison (
*<*,*>*,*<=*,*>=*) -
Equality, Inequality (
*==*,*!=*) -
AND (Bitwise
*&*) -
XOR (Bitwise
*^*) -
OR (Bitwise
*|*) -
Logical AND (
*&&*) -
Logical OR (
*||*) -
Ternary operator (
*<Condition> ? <Statement-Condition-True> : <Statement-Condition-False>*) -
Assignments (
*=*,**=*,*/=*,*%=*,*+=*,*-=*,*<<=*,*>>=*,*&=*,*^=*,*|=*) -
Expression list operator (
*<Statement>*,*<Statement>*)

Having expressions with multiple operators, a suitable aggregation of these with parentheses can alter the default execution precedence order. With statements inside parentheses, the operators’ evaluation can execute with advanced order according to a reasonable chosen structure. This way, the type of reordering may override the standard operations’ evaluation precedence as described in the above listing.

In conclusion, with insight into the numeric representation, a numeric expression starting with 0 natively represents an octal number. Furthermore, a leading 0x or 0X character denotes a hexadecimal number. A number can similarly apply to a different arithmetic base number in the range 2-64, making use of the prefix base#*.* Whenever this prefix is absent, we use by default the standard base 10.

## 3. Alternative Options to the *let* Command

The *let* command provides particular features and enhanced capabilities related to variable assignments and mathematical operations. However, this doesn’t mean that the standard variable assignment approach is ineligible for similar tasks.

With regard to the evaluation of arithmetic operations, **the arithmetic evaluation (( )) and arithmetic expansion $(( )) constructs perform an equivalent work**. Their difference mainly lies in the direct or indirect sense of assignment. We illustrate this fact with the next simple examples:

```
$ a=3; n=50; ((x=a+n)) ; echo $a, $n, $x
3, 50, 53
$ a=3; n=50; x=$((a+n)) ; echo $a, $n, $x
3, 50, 53
```

Generally, we can’t use spaces around the assignment and binary operators. But with the *let* command, we gain the advantage of such convenience by turning on the integer attribute. In the following examples, we can follow how this practically works. For instance, using the remainder operator ‘%’ and the logical bit shifting operator ‘<<‘:

```
$ declare -i customVar1; customVar1="9 % 5"; echo $customVar1
4
$ x=2; declare -i customVar2; customVar2="8 << x"; echo $customVar2
32
```

In this characteristic case, the variables *customVar1* and *customVar2 *assume integer values through the built-in command declare. This command yields the capability of performing arithmetic operations in a *let*-command flavor.

Alternatively, a similar command capable of evaluating arithmetic expressions is the command *expr*. Still, this can comparably perform arithmetic operations, but with the cost of slightly peculiar syntax rules. Let’s inspect these specific properties below.

At first, arithmetic operators must be surrounded by space(s):

```
$ expr 2 + 9
11
```

In addition, specific operators such as the multiplication (*) and the division (/) operators must have as a prefix the escape character ‘\’. For instance:

```
$ expr 3 \* 22
66
```

```
$ expr 15 \/ 4
3
```

Furthermore, number comparison operators need to be used with double quotes:

```
$ x=5; y=18 ; expr $x ">" $y
0
```

Finally, operations with variable expansion occur in the usual mode:

```
$ x=5; y=18 ; expr $x + $y
23
```

## 4. Main Differences With the Standard Assignment Case

The *let* command structure has been implemented with various distinct features that differentiate it from the common assignment operation. Below, we highlight the most significant differences between the two cases:

- Reference of variables without the use of the evaluation parameter expansion prefix symbol ‘$’
- Use of multiple assignments in a single statement (double-quoted syntax type)
- A null value evaluates to 0
- Possibility of arbitrary use of spaces around assignment operator ‘=’ or any binary operators (double-quoted syntax type)
- Local scope environment
- Specific exit code return value depending on the rightmost assigned value
- Possibility of performing arithmetic operations as exponentiation, the remainder calculation (modulo), and logical operations (OR, AND, XOR, bit-shifting, and number reversing)

## 5. Conclusion

In the current article, we explored the most significant characteristics of the *let* command. We performed a detailed inspection of the command typology. In addition, we demonstrated its properties and features through examples for all mentioned subjects.

Proceeding, a comparison is made with the standard corresponding constructs regarding variable assignments and arithmetic operations. In conclusion, we emphasized alternative competitive operators for performing arithmetic evaluations.