Why declare integers? As you know, bash is not like most programming languages. One thing that makes it different is its type system; Unlike static and dynamic typed languages, it allows you to switch type-specific behaviors on/off through variable attributes. Hence, you would declare an integer in bash to make the variable behave more like an integer.
Previously in bash using declare, we covered all the uses for the bash builtin command declare or typeset. Here we focus on the usage for specifying integer variables in bash. For sake of simplicity we will use declare instead of typeset.
As they say, All is string in bash. However, if we hope to escape from characters and strings to use numbers, we may opt to restrict a variable to storing integers like 1 and -1. And in the case that the integer does not know what to do with the value it is being assigned, assign it to 0. To accomplish this in bash, we turn on the integer attribute for the variable using builtins declare
builtin combined with -i
. Makes sense, right? We can also use local
or export
in place of declare if we needed.
Follows are examples of int variable assignment in action.
Assigning an int value without double-quotes would as expected assign a variable the numeric value of the unquoted string. That is, if the unquoted string equals 1, the variable would be assigned the value 1.
_() {
local -i int
## assign int value without quotes
int=1
cecho green "int=1"
cecho yellow "${int}" # 1
}
_
In the last example we assigned int the value of 1 as you would expect, without using quotes. It turns out that quotes do not affect assignment of integer variables in bash if the symbol on the right hand side begins with a digit. You will see later that this is not the case when the right hand side could be a variable name.
_() {
local -i int
## assign int value with quotes
int="2"
cecho green "int=2"
cecho yellow "${int}" # 2
}
_
In the last two examples, we were doing the right thing, actually assigning an int variable a numberical value. What happens if we try to assign it the string two. You may be surprised what you find.
_() {
local var
local -i int
## assign int value of another variable
int=var
cecho green "int=var"
cecho yellow "${int}" # 0
}
_
In the last examples, we assigned in the name of a varaible and saw what happened. Now let's introduce side effects.
_() {
local var
local -i int
## assign int value of another variable with side effects
var=0
cecho green "int=var"
int=var
cecho yellow "${int}" # 0
cecho green "(( var++ ))"
(( var++ ))
cecho green "int=var"
int=var
cecho yellow "${int}" # 1
}
_
It may take some time to split this variable assignment example script into few enough bytes to be easily processed by the reader. Until then, here is the rest of int variable assignment examples.
_() {
local var
local empty_var
local -i int
## assign int value of another variable w/ increment
int=var+1
cecho green "int=var+1"
cecho yellow "${int}" # 4
## assign int value of another variable empty
int=empty_var
cecho green "int=\"empty_var\""
cecho yellow "${int}" # 0
## assign int value of another variable in quotes
int="var"
cecho green "int=\"var\""
cecho yellow "${int}" # 3
## assign int value of another variable random
int="RANDOM"
cecho green "int=\"RANDOM\""
cecho yellow "int: ${int}" # maybe 1234
}
_
Here is the demo script left so that you may see what examples above look like in the whole context of the script. Feel free to skip through as needed.
_() {
local var
local empty_var
local -i int
## assign int value without quote
int=1
cecho green "int=1"
cecho yellow "${int}" # 1
## assign int value with quote
int="2"
cecho green "int=2"
cecho yellow "${int}" # 2
## assign int value of another variable
int=var
cecho green "int=var"
cecho yellow "${int}" # 0
## assign other variable value to int
var=3
cecho green "var=3"
## assign int value of another variable again
int=var
cecho green "int=var"
cecho yellow "${int}" # 3
## assign int value of another variable w/ increment
int=var+1
cecho green "int=var+1"
cecho yellow "${int}" # 4
## assign int value of another variable empty
int=empty_var
cecho green "int=\"empty_var\""
cecho yellow "${int}" # 0
## assign int value of another variable in quotes
int="var"
cecho green "int=\"var\""
cecho yellow "${int}" # 3
## assign int value of another variable random
int="RANDOM"
cecho green "int=\"RANDOM\""
cecho yellow "int: ${int}" # maybe 1234
}
_
Source: 210513-run-example-001-bash-declare-intenger-assignment.sh
Here is what you would expect to see if you were to run the script as bashboy.
$ bashboy run:example-001-bash-declare-intenger-assignment
int=1
1
int=2
2
int=var
0
var=3
int=var
3
int=var+1
4
int="empty_var"
0
int="var"
3
int="RANDOM"
int: 12839
As you see there are some quirks to keep in mind when using the integer attibute in bash, especially in the case when assigning an alphanumeric string not begining with zero matching an existing variable name to an integer variable. However, it is possibly to prevent undefined behavior from occurring by paying attention to scope.
Let's skip to the good part, creating integer variables!
Here is how to declare an integer variable in bash. Note that int
is not a keyword for integer. That is the job of the -i
option to the builtin declare.
There is more than one way to determine if a variable is an integer or not but you could get away with checking if the variable has the -i attribute. After all, an integer variable holds integer values despite what you try to assign to it.
# check if a variable is an integer
declare -p int # declare -i int="1"
We say a variable is an integer if the -i attribute set, i.e. if declare -p returns either -i or -ir. Note that in the case that the variable is found to have the -n attribute set, testing the end nameref is required to determine if the variable is an integer.
# create an integer variable and modify through assignment
unset one # just to be sure
declare -i int # int is an integer
int=5 # int=$(( 5 ))=5
int+=1 # int=$(( int + 1 ))=6
int+=int+1 # int=$(( int + int + 1 ))=13
int=one # int=$(( one ))=
Now look at what happens without the integer attribute.
# result of assignment without integer attribute
unset int # just to be sure
unset one # just to be sure
int=5 # int=5
int+=1 # int=${int}1=51
int+=int+1 # int=${int}+int+1=51int+1
int=one # int=one
Now you see what happens if you forget to declare a variable as an integer. Not something that you want running loose in your program.
declare -i int
We say that if there is a variable named int, it now has the integer attribute set. If the variable named in the declare command is not set yet, then the attribute is applied to a future variable unless unset. Using local instead of declare, causes the attribute to only be applied in subsequent assignments in the local scope.
int=5
Depending on whether the variable has the integer attribute this sets the value of int to the string 5 or arithmetic expansion of 5. In the case of 5, the result yields 5 for both cases.
int+=1
Should increment int if integer attribute set.
int+=int+1
Should double int and increment if integer attribute set.
Definition of types of integer variables.
A variable with the -i attribute set.
We define an integer variable in bash as a variable with the integer attribute set. The question we ask here is, "Is it valid to declare a variable with the integer attribute in combination with another attribute?" This may be expanded later.
An integer variable with the -r attribute set.
A variable with only the -i attribute set.
We define a pure integer variable in bash as a variable with only the integer attribute set.
You may be wondering why you would even bother declaring integer variables in bash. After all, aren't all variable strings? Yes exactly, but using declare to add the integer attribute to a variable affects assignment.
To better understand how and why integers are declared in bash, it is helpful to look at behavior with and without using integer variables.
#!/bin/bash
## test-bash-declare-integer-1
## - fun with integers attributes
## version 0.0.1 - initial
##################################################
test-bash-declare-integer-1() {
## setup
local lonely_integer # one int is fine
local i # for ease of use
declare -n i=lonely_integer # declare attributes
## (1) do something wrong with integers
i=asdf # (1) asdf as i is asdf
echo ${i} # (2) asdf
## (2) what if we give it the integer attribute?
declare -i lonely_integer # (1) declare integer
i=asdf # (2) asdf as i is 0
echo ${i} # (3) 0
## (3) what happened?
declare +i lonely_integer # (1) revert back
i=$(( ${asdf} )) # (2) what happens in (2) under (1) conditions
echo ${i} # (3) 0
## (4) proof: part i (without integer attribute)
asdf=1 # assignment for proof
i=$(( ${asdf} )) # same as (3.2)
echo ${i} # 1
## (5) proof: part ii (with integer attribute)
declare -i lonely_integer # same as (2.1)
i=asdf # asdf as i is 1
echo ${i} # 1
}
##################################################
if [ ${#} -eq 0 ]
then
true
else
exit 1 # wrong args
fi
##################################################
test-bash-declare-integer-1
##################################################
## generated by create-stub2.sh v0.1.1
## on Sun, 03 Feb 2019 17:36:53 +0900
## see <https://github.com/temptemp3/sh2>
##################################################
You see that adding the integer attribute forces arithmetic expressions to be evaluated on assignment.
Here are a few quesrtions related to declaring integers in bash.
Arithmetic binary operators can be used to compare integers in bash in the form arg1 OP arg2
. For example, we may test if two integers are not equal as follows.
test ! ${a} -eq ${b} # a != b
If the variable a and b are attributed with the integer attribute, then nothing unexpected should happen. However, if a or b are not attributed with the integer attribute, then an error may occur as follows.
a="c d"
b="e"
test ${a} -eq ${b} #
#bash: test: too many arguments
echo ${?} # 2
set -v -x
test ${a} -eq ${b} #
#+ test a d -eq c
#bash: test: too many arguments
Declare integer variables using the declare builtin with the -i option.
declare -i myint
Declare integer variables with local bindings using declare in conjunction with the local (1). In the shortest form, the operation may be performed in a single statement (2) consisting of local, the integer attribute option -i, and variable name.
(1), separate local binding and integer attribute assignment
local myint
declare -i myint
or (2), local binding with integer attribute set
local -i myint
Either way works.