bash is written in
C, so one might expect some remnants of
C, but the fact is
bash is very different from it --
bash (or any other shell, for that matter) was never designed to be a
full-fledged programming language.
bash was initially written to execute various *nix
standard commands directly, in essence, an interface to do
exec() (and brothers) calls.
But, from time to time, users tend to (ab)use
bash as a tool for doing stuffs that need a proper language with all the bells and whistles. We all tend to do some ambitious
things in scripts, like introducing an opaque data structure, trying to reference pointers(!), and so on. So,
bash developers have added various functionalities over the years,
which leads to the current state of
bash -- something between a typical shell and a programming language.
By the way,
bash is the official shell of the GNU project, one of the reasons why it is so prevalent in all GNU/Linux distros (comes with the GNU
In this post, I will try to give some pointers on
bash variables, and type related operations on them.
- I'm not counting indexed and associative arrays (introduced in
- I'm using
While working in
bash, interactively or inside scripts, we need to deal with variables, and operators related to it.
bash has some strict, and sort of unusual rules when dealing with variables.
The trick to understanding these would be to never compare
bash shell with a programming language.
bash variables are untyped, they don't have any associated type to begin with. For understanding, you can think of them as all strings (well, not the lower level view, but think it like so for understanding), and depending on the operators they could be treated as integers. So, there is no type or alike you find in typical languages.
bash has a very restrictive variable declaration syntax. There must be no whitespace around
= while declaring variables. Correct way:
While all of the following are incorrect:
foo = bar foo =bar foo= bar
Okay, now the following is not actually setting the variable value as an integer type, but depending on the operator it is used with it would behave as an integer (we'll come to this later):
Some might thought that,
declare -i is actually setting the variable as having integer value, see below:
$ declare -i foo=bar $ echo "$foo" 0
As you can see, any value that does not have the correct base for integers are reset to
0, which is pretty misleading as one would be expecting an error/exception. This could introduce
some hard to catch bugs in your code. For this, I would suggest you to avoid using
declare -i unless you're absolutely sure about what you're doing.
So, how can we do arithmetic and string operations in
bash. More importantly how
bash would differentiate between these as the variables are untyped.
The answer is: By using different syntaxes and operators for them. And
bash will set the appropriate operating environment when parsing the tokens.
Let's go through the common operators and syntaxes one by one.
It is a
bashbuiltin; defined by POSIX so portable
[is analogous to
There is also an external command
test(comes with GNU
coreutils, which behaves the similar way as the builtin)
For doing arithmetic operations, you can use operators like
-ge (greater or equal),
-le (less or equal):
$ [ 6 -gt 5 ] && echo "True" || echo "False" True $ [ 6 -eq 5 ] && echo "True" || echo "False" False $ [ 6 = 5 ] && echo "True" || echo "False" False $ [ 5 -le 7 ] && echo "True" || echo "False" True
bash checks the operators to
[, if it is an arithmetic operator, it will expect integers as operands, otherwise will throw an error:
$ [ foo -eq 7 ] bash: [: foo: integer expression expected
Just to note, in
bash and any other shell, the POSIX defined way to compare for equality (for both integers and strings) is
Any other comparison operators are usable on only strings.
It is a
Was introduced to add functionalities
[[can be thought of as a superset of
[supports, and adds many more
While you do not get much for arithmetic operations, but for string operations
[[ is great as it supports shell Glob matching and Regex (Regular Expression) based matching out of the box:
$ bar="spamegg" $ [ "$bar" = spam* ] && echo "Match" || echo "No match" bash: [: too many arguments No match
As you can see, we have set variable
bar as having value
spamegg. While doing shell Glob matching in
[, with Glob token
* (matches zero or more characters),
[ fails giving the error about
too many arguments received.
Note: The string
No match still got printed as
[ exits with status
1 (failed); this happened due to the way we've chained the short-circuit (logical) operators. Here, the
echo "Match" would be executed if
[ exits with a status
0 -- note the
&& (logical AND); and
echo "No match" would be executed if any one of the
echo "Match" fails -- note the
|| (logical OR) operator.
Now, let's check if
[[ can do Glob matching:
$ [[ "$bar" = spam* ]] && echo "Match" || echo "No match" Match
Hmmm, it does. Note that, we're using same
= operator here, for Glob matching too.
== are replaceable in
Let's see Regex matching, the operator changes from
$ [[ "$bar" =~ ^spam ]] && echo "Match" || echo "No match" Match
It does do Regex match.
As mentioned earlier, the arithmetic operators and operations are similar to that of plain
Note: You don't need to strictly quote the variables/parameters when used inside
$ spam='egg foo' $ [ $spam = 'egg foo' ] && echo "True" || echo "False" bash: [: too many arguments False $ [ "$spam" = 'egg foo' ] && echo "True" || echo "False" True $ [[ $spam = 'egg foo' ]] && echo "True" || echo "False" True
Used for arithmetic operations only, should not be used for string operations
Support operator syntaxes like
-ge, and alikes
The eqality operator is not
=, rather you must use
Here is why you should not use
(( on strings:
$ (( foo = foo )) && echo "True" || echo "False" False $ (( foo == foo )) && echo "True" || echo "False" True
Note just the addition of
(( changing the whole thing.
Now, arithmetic operations:
$ spam=5 $ (( spam >= 2 )) && echo "True" || echo "False" True $ (( spam == 2 )) && echo "True" || echo "False" False $ (( spam < 2 )) && echo "True" || echo "False" False
Notice that, I have not used
$ before variable name here, because you don't need that inside
(( spam >= 2 )) and
(( $spam >= 2 )) both are syntactically correct and equivalent.
Note: You can't use the
-eq and alikes inside
$ (( 5 -eq 3 )) bash: ((: 5 -eq 3 : syntax error in expression (error token is "3 ")
One final note would be to use only
[ when you want to make your script portable (e.g. would be run as
/bin/sh and you don't know where
sh is symlinked to) as it is defined
by POSIX and should be available on all Bourne like shells. And if you're sure that you would only run the script with
bash, you can (and you should) leverage the great features