Older UNIX shells like Borne shell and ksh88 have clumsy, inefficient way of doing arithmetic
based on external expr command:

z=`expr $z + 3`

This was questionable decision even at the time when Unix run of machines with just 2MB of memory.
Even at this time shell can be implemented in
Forth (which was available
since early 70th) with much richer capabilities. Charles Moore and Elizabeth Rather formed FORTH, Inc.
in 1973, refining and porting Forth systems to dozens of platforms. Forth interpreter is really small;
minimal is under
1KB.

When Perl arrived, it became clear that classic Unix shell approach to arithmetic expressions is
inferior on modern computers and actually increases overhead of shell instead of decreasing it. So ksh93
was created to fight back the lost ground. While it failed to stop Perl momentum and Perl replaced shell
for larger scripts, ksh93 was a big improvement over all previous shells. Among innovation brought to
shell world by ksh93 is this new type of arithmetic expressions.

Later bash (starting from version 3.2) re-implemented most of the advanced features of ksh93 plus
a couple of its own. Currently bash is restricted to integer arithmetic, while ksh93 can do floating-point
arithmetic as well.

In bash version 3.2 and later you can (and should) use $(( )) or let for integer
arithmetic expressions and
conditionals.
The idea of ((...))
construct is similar to
[[...]]
construct introduced in ksh88. It provides built in capabilities for arithmetic that
are superior to calling external function expr (or test in case of conditionals).

You can also declare integer variable using declare statement, for example:

declare -i k;

In this case you can omit((...))brackets and write expression like in "normal" programming languages. For example

declare -i k=0;
declare -i j=10
k=k+j;

All-in-all despite warts and non-uniformity with the rest of bash expression, ((..)) capabilities represent a considerable
improvement over what we used to have in older Unix shells and thus should be used whenever possible.
I would like to remind again that before ksh93 introduced let and (( ... )) commands, the only
way to perform arithmetic was with expr. For example, to do the same increment to variable
i you need to write:

i=`expr $i + 5`

The ((...)) construct can (and should) be used in any control statements such as if, headers
of the while loop and case statement.

The ((...)) conditional expression evaluates an arithmetic expression like it
was written in a "normal" algorithmic language (you can even omit dollar signs from the variables within
such an expression) and sets the exit status to 1 if the expression evaluates to 0, or to 0 if the expression
evaluates to a non-zero value. For example:

(( uid == 0 ))
(( uid > 1000 ))
(( i=i+1 ))

You can use multiple expressions connected with operations AND or OR. That make it the new standard
was to program integer comparisons in if statements, while loops and similar control flow constructs.
Old [[...]] construct should no longer be used for arithmetic expressions: it just does not
make any sense anymore outside string comparisons. For example

By default, shell variables are typeless and can be assigned both numeric
and non-numeric values. You need explicitly declare integer variable for shell to know how
they should be treated.

While this is not required, a good programming style is to declare all integer variables you use with the
declare
statement. That means that each variable name will appear in the script at least twice and that allow
easy catch misspelling, which are the most common source of errors in bash script. such errors
are difficult to defect because your test case might not cover the branch in which misspelled name
is present. So it will be detect much later, possibly in production.

For novices and infrequent users of Bash this is actually a "must" requirement: all variables should be declared using the Bash
declare command and option set-o nounset should be set at the very beginning
of the script the script. For example to declare a variable named cores, as integer
variable use this:

set-o nounset

declare -i cores
declare message

Choosing good variable names is important. The are two requirements here:

The name should bemnemonic. icount is a better bane than
ic. But excessively long names are prone to typos, So there is some middle ground.
for example number_of_recored_in_the file is way too long.

Names should
also be consistent. Variable names begin with a leading alphabetic or underscore character followed by additional alphanumeric
characters or underscores. In large and complex scripts, you can use names starting with letters
i,k,l,m,n like in Fortran IV to make this type of variables more easy to detect. For
example iCount. In this case if you see the statement iCount='up'
you instantly suspect that something is wrong. Actually bash will produce error message if this
statement is executed.

Although variables can be in upper- or lowercase, tradition dictates that all exported variables
and read-only variables (constants) are
named in uppercase. You can declare a variable read only using option -r in
declare statement.

There are no reserved words, which are words that are reserved for a specific purpose
in the shell but it is a bad idea to use them as variable names.

Because Bash does minimum checking of variable names, even with the nounset option turned
on, another common mistake is using a variable that looks the same. You can declared a variable called
nfiles, but in the body of his program
use the variable nofiles. So it makes sense to initialize all variables in declare
statement

set-o nounset

declare -i cores=16

printf %d $ncores

Here ncores variable is not initialized and will be detected.

NOTES:

The dollar sign means “take value of” The shell replaces $cores with the value of memory
location cores represents. Bash differentiates between a variable's name and the value the variable represents. To refer
to the value of a variable, you must precede the name with a dollar sign ($). this
convertin of variable name into the value is called de-referencing. Bash can perform only
one level of dereferencing. Unlike Perl, there is no pointers to the variables in Bash.

Although printf %d prints a zero for both a value of zero and an empty string, Bash
considers the two values to be different. A variable with no value is not the same as a variable
with a value of zero.

To assign an empty string to
a variable, don't supply any value or use '', for example message=''. Otherwise, include some text or number to be assigned.

Values can be assigned an initial value when the variable is first declared.

set-o nounset

declare -i cores=16
printf "%d" $cores

Because declare is a command, variables are created only when the declare command
is executed.

The results of a command can also be assigned to a variable. If a command is contained in backquotes
('), everything written to standard output is stored in the variable being assigned instead.
For example:

All arithmetic operations are performed on integers. Syntax is similar to conditional expressions
with some twists. After all Bash is based on Born shall which is a product of 70th. And as such has
a lot of warts. You have at least two "wrong" ways of writing arithmetic expression in bash :-).
Unless you need compatibility with versions earlier then 2.0 (Typing
variables declare or typeset ), the only
right way is to declare all integer variables and use
((...)) notation for complex expressions and let for simple (insluding self-referncial operatiors
++, --, etc).

Bash is really non-orthogonal language, in a sense much worse then Perl. Those additonas implemented
during more then 50 years of its existence make the language a real minefield. But we can do nothing
about it.

That means that when there are multiple ways to write the same expression in bash, each
with its own gotachs you need to use the most simple and direct way:

The most "gotch free was is to use ((..)) for the whole assignment statement.
But agaiag if expression is way to complex and uses indirect calls to expr, you need to split it on
a simpler chunks to avoid trobles.

for self-referncial operators always use let. for more complex staff there are subtle nuances here that can spoit
the broth. And in this sense ((...)) is more reliable option. In all cases I mentioned,
Bash supports C-style shotcuts ++, --,+=, etc. For example:

(( i=i+1 )) is the same as (( i++ ))
let i+=1 is the same as let i++
i=$(( i++)) is the same as let i++ but worse then previous example

Quotes or backslashes usually are not needed inside of the $((
)) construct:all characters between the (( and )) are treated as a string in double quotes.

If you use let for more or less complex expression involving multiplication and brackets ( and I do not recommend using let for anything but C-style
shortcut
operators like += and ++) it is prudent to put expression in double quote to avoid conflict
with bash meta symbols such as "*", '(' and ')'.

The best is to declare integer variables and use ((..)) notation.
I do
not recommend using let for anything but C-style self-referecial shorcuts operators like += and ++)

Again, most of C-style shortcuts operators are now supported by BASH. The most useful is probably
+= (familiar to any C programmer). That means that:

declare -i i
i=i+5

is better written as

declare -i i
let i+=5

of cause each integer variable declaretion is need in a script only once.

All C-style self-rederencial shorcur are now available in bash including increments/decrements ++/-- :

While constructs are almost identical there some minor differences. First of all explicit
declation and then usage of "normal" programming language notation implicitly means that you use
((...)) notation. But you better be explicit. You are more or les safe is the expression is
normal with = sign. Like

I=k+1

You can run into troubles using normal notation for self-referential operators. Here you always
need to use let. In other words, writing

i++

is "gotcha" in bash 4.2 if you enabled autocd option in your profile
:-(

but in general "normal way of writing assignment statement in which the valuable on the left side
of the assignement sign is declared as intergeg ahousl always be equavalent to ((...)). There should
be no gotchas here.

I would prefer always use ((..))
in both conditional expressions and assignment statements just to be safe, and you let construct
only for self referential operators.

There are some subtle nuances when you use let with compex expression including a couple of
unpleasant gotchas:

The let statement requires that there be no spaces around not only the assignment operator
(the equal sign), but around any of the other operators as well: it must all be packed together
into a single word.You need to use quotes if you want to use spaces between
tokens of the expression, for example

let "i = i + 1"

That is definitely a kludge, as it is clear that this is a typical problems of "language
within the language".

At the same time, some short assignment statements like k=$(( i++ )) can
be more clearly written using let. But beware Gotchas with metacharacters like "*": you
can't rewrite i=$(( i*2 )) as let i=i*2, because * in a metasymbol in Bash.
Only as

let "i=i*2"

Otherwise * will be interpreted as wildcard. As string between (( and )) is considered to be included
in double quotes, in most more or less complex cases the ((...)) command is more convenient
to use than let.

String between (( and )) is considered to be included in double quotes which makes the
((...)) command more convenient to use thanlet.

The main case where let construct is more convenient and more natural then
((..)) construct is C-style shortcuts like ++, +=,etc. For example:

let i++ # clear and concise

This commands is equivalent to (( i=i + 1 )) as well as to (( i++ )) and looks
slightly more natural. Similar situation exists with

let i+=5

you can write it as to (( i=i+5 )) but does this really make it more clear ?

Note: there can be some exotic usage of (( ... ))). For example, the assignments can be cascaded, through the use of the comma operator:

echo $(( i+=2 , j++ ))

which will do both assignments and then echo the result of the second expression (since
the comma operator returns the value of its second expression).

This will avoid executing the part after || by accident when the code between && and || fails.

If the condition is merely checking if a variable is set, there's even a shorter form:
a=${VAR:-20}

will assign to a the value of VAR if VAR is set, otherwise it will assign it the default value 20
-- this can also be a result of an expression. As alex notes in the comment, this approach is
technically called "Parameter Expansion".

(( a = b==5 ? c : d )) # string + numeric

This is good for numeric comparisons and assignments, but it will give unpredictable results if
you use it for string comparisons and assignments.... (( )) treats any/all strings as 0 – Peter.O May 12 '11 at 22:51

The let command supports most of the basic operators one would need:
let a = b==5 ? c : d;

Naturally, this works only for assigning variables; it cannot execute other commands.

it is exactly equivalent to (( ... )), so it is only valid for arithmetic expressions – AlberT Jan 30 '13 at 12:07

Here is another option where you only have to specify the variable you're assigning once, and it
doesn't matter whether what your assigning is a string or a number:VARIABLE=`[ test ] && echo VALUE_A || echo VALUE_B`

Just a thought. :)

down vote

if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

The cond && op1 || op2 solution suggested in other answers has an inherent bug: if op1 has a
nonzero exit status, op2 silently becomes the result; the error will also not be catched in -e
mode.

(( )) treats any strings as 0. If any of the integer variable is accidentally
assigned a string value and you did not declare this variable as integer and strict
checking option you might have a
problem, that can slip during testing and discovered much later in production.

Generally in shells like bash or ksh93 spaces are treated as in any normal algorithmic language:
they are optional. But there are some exception. Spaces before and after equal sign have
a special meaning in shell. And that a gotcha that you need to be aware of: a space before equal sign
confuses bash into thinking that this is a function invocation. For example, in case of

i = $(( i + 5 )) # not what you think!

bash will try to run a program named i and its first argument would be an equal sign, and
its second argument would be the number you get adding 5 to the value of $i.

Spaces before and after equal sign have a special meaning:a space before
equal sign confuses bash into thinking that this is a function invocation

Another idiosyncrasy is that so called shell R-value rule ($ should be prefixed to the
shell variable on the right side of assignment statement to get its value) is now optional and generally
you should probably omit $ prefix for variables in ((..)) construct. That means that

let i=$i+1

can be written as in regular programming languages:

let i=i+1

The gotcha is that you still need the dollar sign for positional parameters (e.g.,$2)
to distinguish it from a numeric constant (e.g., "2"). Here's an example:

“In early proposals, a form $[expression] was used. It was functionally equivalent to the "$(())"
of the current text, but objections were lodged that the 1988 KornShell had already implemented "$(())"
and there was no compelling reason to invent yet another syntax.

Furthermore, the "$[]" syntax had
a minor incompatibility involving the patterns in case statements.”

Notable quotes:

"... Backquotes ( ` ` ) are old-style form of command substitution, with some differences: in this form, backslash retains its literal meaning except when followed by $ , ` , or \ , and the first backquote not preceded by a backslash terminates the command substitution; whereas in the $( ) form, all characters between the parentheses make up the command, none are treated specially. ..."

"... Double square brackets delimit a Conditional Expression. And, I find the following to be a good reading on the subject: "(IBM) Demystify test, [, [[, ((, and if-then-else" ..."

What you've written actually almost works (it would work if all the variables were numbers), but
it's not an idiomatic way at all.

( ) parentheses indicate a
subshell . What's inside them isn't an expression like in many other languages. It's a
list of commands (just like outside parentheses). These commands are executed in a separate
subprocess, so any redirection, assignment, etc. performed inside the parentheses has no effect
outside the parentheses.

With a leading dollar sign, $( ) is a
command substitution : there is a command inside the parentheses, and the output from
the command is used as part of the command line (after extra expansions unless the substitution
is between double quotes, but that's
another story ).

{ } braces are like parentheses in that they group commands, but they only
influence parsing, not grouping. The program x=2; { x=4; }; echo $x prints 4,
whereas x=2; (x=4); echo $x prints 2. (Also braces require spaces around them
and a semicolon before closing, whereas parentheses don't. That's just a syntax quirk.)

With a leading dollar sign, ${VAR} is a
parameter expansion , expanding to the value of a variable, with possible extra transformations.

(( )) double parentheses surround an
arithmetic instruction , that is, a computation on integers, with a syntax resembling other
programming languages. This syntax is mostly used for assignments and in conditionals.

The same syntax is used in arithmetic expressions $(( )) , which expand
to the integer value of the expression.

[[ ]] double brackets surround
conditional expressions . Conditional expressions are mostly built on
operators such as -n $variable to test if a variable is empty and -e
$file to test if a file exists. There are also string equality operators: "$string1"
= "$string2" (beware that the right-hand side is a pattern, e.g. [[ $foo = a*
]] tests if $foo starts with a while [[ $foo = "a*"
]] tests if $foo is exactly a* ), and the familiar !
, && and || operators for negation, conjunction and disjunction as
well as parentheses for grouping.

Note that you need a space around each operator (e.g. [[ "$x" = "$y" ]]
, not [[ "$x"="$y" ]] ), and a space or a character like ;
both inside and outside the brackets (e.g. [[ -n $foo ]] , not [[-n
$foo]] ).

[ ] single brackets are an alternate form of conditional expressions with
more quirks (but older and more portable). Don't write any for now; start worrying about them
when you find scripts that contain them.

This is the idiomatic way to write your test in bash:

if [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]; then

If you need portability to other shells, this would be the way (note the additional quoting
and the separate sets of brackets around each individual test):

+1 @WillSheppard for yr reminder of proper style. Gilles, don't you need a semicolon after yr
closing curly bracket and before "then" ? I always thought if , then
, else and fi could not be on the same line... As in:

Backquotes ( ` ` ) are old-style form of command substitution, with some differences:
in this form, backslash retains its literal meaning except when followed by $ ,
` , or \ , and the first backquote not preceded by a backslash terminates
the command substitution; whereas in the $( ) form, all characters between the parentheses
make up the command, none are treated specially.

You could emphasize that single brackets have completely different semantics inside and outside
of double brackets. (Because you start with explicitly pointing out the subshell semantics but
then only as an aside mention the grouping semantics as part of conditional expressions. Was confusing
to me for a second when I looked at your idiomatic example.) –
Peter A. Schneider
Aug 28 at 13:16

Just to be sure: The quoting in 't1' is unnecessary, right? Because as opposed to arithmetic instructions
in double parentheses, where t1 would be a variable, t1 in a conditional expression in double
brackets is just a literal string.

Bash has several different ways to say we want to do arithmetic instead of string
operations. Let's look at them one by one.

The first way is the let command:

$ unset a; a=4+5
$ echo $a
4+5
$ let a=4+5
$ echo $a
9

You may use spaces, parentheses and so forth, if you quote the expression:

$ let a='(5+2)*3'

For a full list of operators availabile, see help let or the manual.

Next, the actual arithmetic evaluation compound command syntax:

$ ((a=(5+2)*3))

This is equivalent to let , but we can also use it as a command , for
example in an if statement:

$ if (($a == 21)); then echo 'Blackjack!'; fi

Operators such as == , < , > and so on cause a comparison
to be performed, inside an arithmetic evaluation. If the comparison is "true" (for example,
10 > 2 is true in arithmetic -- but not in strings!) then the compound command
exits with status 0. If the comparison is false, it exits with status 1. This makes it suitable
for testing things in a script.

Although not a compound command, an arithmetic substitution (or arithmetic
expression ) syntax is also available:

$ echo "There are $(($rows * $columns)) cells"

Inside $((...)) is an arithmetic context , just like with ((...))
, meaning we do arithmetic (multiplying things) instead of string manipulations (concatenating
$rows , space, asterisk, space, $columns ). $((...)) is also
portable to the POSIX shell, while ((...)) is not.

Readers who are familiar with the C programming language might wish to know that
((...)) has many C-like features. Among them are the ternary operator:

$ ((abs = (a >= 0) ? a : -a))

and the use of an integer value as a truth value:

$ if ((flag)); then echo "uh oh, our flag is up"; fi

Note that we used variables inside ((...)) without prefixing them with $
-signs. This is a special syntactic shortcut that Bash allows inside arithmetic evaluations and
arithmetic expressions.

There is one final thing we must mention about ((flag)) . Because the inside of
((...)) is C-like, a variable (or expression) that evaluates to zero will be
considered false for the purposes of the arithmetic evaluation. Then, because the
evaluation is false, it will exit with a status of 1. Likewise, if the expression
inside ((...)) is non-zero , it will be considered true ; and since
the evaluation is true, it will exit with status 0. This is potentially very
confusing, even to experts, so you should take some time to think about this. Nevertheless,
when things are used the way they're intended, it makes sense in the end:

A single space can make a huge difference in bash :-)

I feel stupid: declare not found in bash scripting? I was anxious to get my feet wet, and I'm
only up to my toes before I'm stuck...this seems very very easy but I'm not sure what I've done
wrong. Below is the script and its output. What the heck am I missing?

I ran what you posted (but at the command line, not in a script, though that should make no
significant difference), and got this:

Code:

-bash: ${ PROD[*]}: bad substitution

In other words, I couldn't reproduce your first problem, the "declare: not found" error. Try
the declare command by itself, on the command line.

And I got rid of the "bad substitution" problem when I removed the space which is between the
${ and the PROD on the printf line.

Hope this helps.

blackhole54

The previous poster identified your second problem.

As far as your first problem goes ... I am not a bash guru although I have written a number
of bash scripts. So far I have found no need for declare statements. I suspect that you might
not need it either. But if you do want to use it, the following does work:

EDIT: My original post was based on an older version of bash. When I tried the declare statement
you posted I got an error message, but one that was different from yours. I just tried it on a
newer version of bash, and your declare statement worked fine. So it might depend on the version
of bash you are running. What I posted above runs fine on both versions.

By gum, you're right! Then I get readonly warning on second declare, which is
reasonable, and the function completes. The xtrace output is also interesting;
implies
declare
without single quotes is really treated as two steps.
Ready to become superstitious about always single-quoting the argument to
declare
. Hard to see how popping the function stack can be anything
but a bug, though. –
Ron Burk
Jun 14 '15 at 23:58

Well, rats. I think your answer is correct, but also reveals I'm really asking
two separate questions when I thought they were probably the same issue. Since the
title better reflects what turns out to be the "other" question, I'll leave this up
for a while and see if anybody knows what's up with the mysterious implicit
function return... Thanks! –
Ron Burk
Jun 14 '15 at 17:01

Edited question to focus on the remaining issue. Thanks again for the answer on
the "-v" issue with associative arrays. –
Ron Burk
Jun 14 '15 at 17:55

Accepting this answer. Complete answer is here plus your comments above plus
(IMHO) there's a bug in this version of bash (can't see how there can be any excuse
for popping the function stack without warning). Thanks for your excellent research
on this! –
Ron Burk
Jun 21 '15 at 19:31

The
declare
or
typeset
builtins
, which are exact
synonyms, permit modifying the properties of variables. This is a very weak form of the
typing
[1]
available in certain
programming languages. The
declare
command is specific to version 2 or later of Bash.
The
typeset
command also works in ksh scripts.

declare/typeset options

-r
readonly

(
declare -r var1
works the same as
readonly var1
)

This is the rough equivalent of the
C
const
type qualifier. An attempt to
change the value of a
readonly
variable fails with an error message.

Purpose An array is a parameter that holds mappings from keys to values. Arrays are used
to store a collection of parameters into a parameter. Arrays (in any programming language) are a
useful and common composite data structure, and one of the most important scripting features in Bash
and other shells.

Here is an abstract representation of an array named NAMES . The indexes go from
0 to 3.

NAMES
0: Peter
1: Anna
2: Greg
3: Jan

Instead of using 4 separate variables, multiple related variables are grouped grouped together
into elements of the array, accessible by their key . If you want the second name,
ask for index 1 of the array NAMES . Indexing Bash supports two different types
of ksh-like one-dimensional arrays. Multidimensional arrays are not implemented .

Indexed arrays use positive integer numbers as keys. Indexed arrays are always sparse
, meaning indexes are not necessarily contiguous. All syntax used for both assigning and dereferencing
indexed arrays is an
arithmetic evaluation context (see
Referencing
). As in C and many other languages, the numerical array indexes start at 0 (zero). Indexed arrays
are the most common, useful, and portable type. Indexed arrays were first introduced to Bourne-like
shells by ksh88. Similar, partially compatible syntax was inherited by many derivatives including
Bash. Indexed arrays always carry the -a attribute.

Associative arrays (sometimes known as a "hash" or "dict") use arbitrary nonempty
strings as keys. In other words, associative arrays allow you to look up a value from a table
based upon its corresponding string label. Associative arrays are always unordered , they merely
associate key-value pairs. If you retrieve multiple values from the array at once, you
can't count on them coming out in the same order you put them in. Associative arrays always carry
the -A attribute, and unlike indexed arrays, Bash requires that they always be declared
explicitly (as indexed arrays are the default, see
declaration
). Associative arrays were first introduced in ksh93, and similar mechanisms were later adopted
by Zsh and Bash version 4. These three are currently the only POSIX-compatible shells with any
associative array support.

SyntaxReferencing To accommodate referring to array variables and their individual
elements, Bash extends the parameter naming scheme with a subscript suffix. Any valid ordinary scalar
parameter name is also a valid array name: [[:alpha:]_][[:alnum:]_]* . The parameter
name may be followed by an optional subscript enclosed in square brackets to refer to a member of
the array.

The overall syntax is arrname[subscript] - where for indexed arrays, subscript
is any valid arithmetic expression, and for associative arrays, any nonempty string. Subscripts are
first processed for parameter and arithmetic expansions, and command and process substitutions. When
used within parameter expansions or as an argument to the
unset builtin,
the special subscripts * and @ are also accepted which act upon arrays
analogously to the way the @ and * special parameters act upon the positional
parameters. In parsing the subscript, bash ignores any text that follows the closing bracket up to
the end of the parameter name.

With few exceptions, names of this form may be used anywhere ordinary parameter names are valid,
such as within arithmetic
expressions , parameter expansions
, and as arguments to builtins that accept parameter names. An array is a Bash parameter
that has been given the -a (for indexed) or -A (for associative) attributes
. However, any regular (non-special or positional) parameter may be validly referenced using a subscript,
because in most contexts, referring to the zeroth element of an array is synonymous with referring
to the array name without a subscript.

The only exceptions to this rule are in a few cases where the array variable's name refers to
the array as a whole. This is the case for the unset builtin (see
destruction
) and when declaring an array without assigning any values (see
declaration
). Declaration The following explicitly give variables array attributes, making them arrays:

Syntax

Description

ARRAY=()

Declares an indexed array ARRAY and initializes it to be empty. This can also
be used to empty an existing array.

ARRAY[0]=

Generally sets the first element of an indexed array. If no array ARRAY existed
before, it is created.

declare -a ARRAY

Declares an indexed array ARRAY . An existing array is not initialized.

declare -A ARRAY

Declares an associative array ARRAY . This is the one and only way to create
associative arrays.

Storing values Storing values in arrays is quite as simple as storing values in normal variables.

As above. If no index is given, as a default the zeroth element is set to VALUE
. Careful, this is even true of associative arrays - there is no error if no key is specified,
and the value is assigned to string index "0".

ARRAY=(E1 E2 )

Compound array assignment - sets the whole array ARRAY to the given list of
elements indexed sequentially starting at zero. The array is unset before assignment unless
the += operator is used. When the list is empty ( ARRAY=() ), the array will be
set to an empty array. This method obviously does not use explicit indexes. An associative
array can not be set like that! Clearing an associative array using ARRAY=() works.

ARRAY=([X]=E1 [Y]=E2 )

Compound assignment for indexed arrays with index-value pairs declared individually (here
for example X and Y ). X and Y are arithmetic expressions. This syntax
can be combined with the above - elements declared without an explicitly specified index are
assigned sequentially starting at either the last element with an explicit index, or zero.

ARRAY=([S1]=E1 [S2]=E2 )

Individual mass-setting for associative arrays . The named indexes (here: S1
and S2 ) are strings.

Expands to the value of the index N in the indexed array ARRAY
. If N is a negative number, it's treated as the offset from the maximum assigned
index (can't be used for assignment) - 1

${ARRAY[S]}

Expands to the value of the index S in the associative array ARRAY
.

"${ARRAY[@]}"
${ARRAY[@]}
"${ARRAY[*]}"
${ARRAY[*]}

Similar to
mass-expanding
positional parameters , this expands to all elements. If unquoted, both subscripts
* and @ expand to the same result, if quoted, @ expands
to all elements individually quoted, * expands to all elements quoted as a whole.

"${ARRAY[@]:N:M}"
${ARRAY[@]:N:M}
"${ARRAY[*]:N:M}"
${ARRAY[*]:N:M}

Similar to what this syntax does for the characters of a single string when doing
substring
expansion , this expands to M elements starting with element N
. This way you can mass-expand individual indexes. The rules for quoting and the subscripts
* and @ are the same as above for the other mass-expansions.

For clarification: When you use the subscripts @ or * for mass-expanding,
then the behaviour is exactly what it is for $@ and $* when
mass-expanding
the positional parameters . You should read this article to understand what's going on. Metadata

Syntax

Description

${#ARRAY[N]}

Expands to the length of an individual array member at index N ( stringlength

${#ARRAY[STRING]}

Expands to the length of an individual associative array member at index STRING
( stringlength )

${#ARRAY[@]}${#ARRAY[*]}

Expands to the number of elements in ARRAY

${!ARRAY[@]}${!ARRAY[*]}

Expands to the indexes in ARRAY since BASH 3.0

Destruction The
unset builtin command
is used to destroy (unset) arrays or individual elements of arrays.

Example: You are in a directory with a file named x1 , and you want to destroy an
array element x[1] , with

unset x[1]

then pathname expansion will expand to the filename x1 and break your processing!

Even worse, if nullglob is set, your array/index will disappear.

To avoid this, always quote the array name and index:

unset -v 'x[1]'

This applies generally to all commands which take variable names as arguments. Single quotes preferred.

UsageNumerical Index Numerical indexed arrays are easy to understand and easy to
use. The Purpose
and Indexing chapters
above more or less explain all the needed background theory.

Now, some examples and comments for you.

Let's say we have an array sentence which is initialized as follows:

sentence=(Be liberal in what you accept, and conservative in what you send)

Since no special code is there to prevent word splitting (no quotes), every word there will be
assigned to an individual array element. When you count the words you see, you should get 12. Now
let's see if Bash has the same opinion:

$ echo ${#sentence[@]}
12

Yes, 12. Fine. You can take this number to walk through the array. Just subtract 1 from the number
of elements, and start your walk at 0 (zero)

You always have to remember that, it seems newbies have problems sometimes. Please understand
that numerical array indexing begins at 0 (zero)

The method above, walking through an array by just knowing its number of elements, only works
for arrays where all elements are set, of course. If one element in the middle is removed, then the
calculation is nonsense, because the number of elements doesn't correspond to the highest used index
anymore (we call them " sparse arrays "). Associative (Bash 4) Associative arrays
(or hash tables ) are not much more complicated than numerical indexed arrays. The numerical
index value (in Bash a number starting at zero) just is replaced with an arbitrary string:

This effectively means, you can get the data back with "${sentence[@]}" , of course
(just like with numerical indexing), but you can't rely on a specific order. If you want to store
ordered data, or re-order data, go with numerical indexes. For associative arrays, you usually query
known index values:

Integer arrays Any type attributes applied to an array apply to all elements of the array.
If the integer attribute is set for either indexed or associative arrays, then values are considered
as arithmetic for both compound and ordinary assignment, and the += operator is modified in the same
way as for ordinary integer variables.

a[0] is assigned to the result of 2+4 . a[1] gets the result
of 2+2 . The last index in the first assignment is the result of a[2] ,
which has already been assigned as 4 , and its value is also given a[2]
.

This shows that even though any existing arrays named a in the current scope have
already been unset by using = instead of += to the compound assignment,
arithmetic variables within keys can self-reference any elements already assigned within the same
compound-assignment. With integer arrays this also applies to expressions to the right of the
= . (See
evaluation
order , the right side of an arithmetic assignment is typically evaluated first in Bash.)

The second compound assignment argument to declare uses += , so it appends after
the last element of the existing array rather than deleting it and creating a new array, so
a[5] gets 42 .

Lastly, the element whose index is the value of a[4] ( 4 ), gets
3 added to its existing value, making a[4] == 7 . Note that
having the integer attribute set this time causes += to add, rather than append a string, as it would
for a non-integer array.

The single quotes force the assignments to be evaluated in the environment of declare
. This is important because attributes are only applied to the assignment after assignment arguments
are processed. Without them the += compound assignment would have been invalid, and
strings would have been inserted into the integer array without evaluating the arithmetic. A special-case
of this is shown in the next section.

eval , but there are differences.) 'Todo: ' Discuss this in detail.

Indirection Arrays can be expanded indirectly using the indirect parameter expansion syntax.
Parameters whose values are of the form: name[index] , name[@] , or
name[*] when expanded indirectly produce the expected results. This is mainly useful
for passing arrays (especially multiple arrays) by name to a function.

This example is an "isSubset"-like predicate which returns true if all key-value pairs of the
array given as the first argument to isSubset correspond to a key-value of the array given as the
second argument. It demonstrates both indirect array expansion and indirect key-passing without eval
using the aforementioned special compound assignment expansion.

This script is one way of implementing a crude multidimensional associative array by storing array
definitions in an array and referencing them through indirection. The script takes two keys and dynamically
calls a function whose name is resolved from the array.

callFuncs() {
# Set up indirect references as positional parameters to minimize local name collisions.
set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'}
# The only way to test for set but null parameters is unfortunately to test each individually.
local x
for x; do
[[ $x ]] || return 0
done
local -A a=(
[foo]='([r]=f [s]=g [t]=h)'
[bar]='([u]=i [v]=j [w]=k)'
[baz]='([x]=l [y]=m [z]=n)'
) ${4+${a["$1"]+"${1}=${!3}"}} # For example, if "$1" is "bar" then define a new array: bar=([u]=i [v]=j [w]=k)
${4+${a["$1"]+"${!4-:}"}} # Now just lookup the new array. for inputs: "bar" "v", the function named "j" will be called, which prints "j" to stdout.
}
main() {
# Define functions named {f..n} which just print their own names.
local fun='() { echo "$FUNCNAME"; }' x
for x in {f..n}; do
eval "${x}${fun}"
done
callFuncs "$@"
}
main "$@"

Bugs and Portability Considerations

Arrays are not specified by POSIX. One-dimensional indexed arrays are supported using similar
syntax and semantics by most Korn-like shells.

In Ksh93, arrays whose types are not given explicitly are not necessarily indexed. Arrays
defined using compound assignments which specify subscripts are associative by default. In Bash,
associative arrays can only be created by explicitly declaring them as associative, otherwise
they are always indexed. In addition, ksh93 has several other compound structures whose types
can be determined by the compound assignment syntax used to create them.

In Ksh93, using the = compound assignment operator unsets the array, including
any attributes that have been set on the array prior to assignment. In order to preserve attributes,
you must use the += operator. However, declaring an associative array, then attempting
an a=( ) style compound assignment without specifying indexes is an error. I can't
explain this inconsistency.

Only Bash and mksh support compound assignment with mixed explicit subscripts and automatically
incrementing subscripts. In ksh93, in order to specify individual subscripts within a compound
assignment, all subscripts must be given (or none). Zsh doesn't support specifying individual
subscripts at all.

Appending to a compound assignment is a fairly portable way to append elements after the last
index of an array. In Bash, this also sets append mode for all individual assignments within the
compound assignment, such that if a lower subscript is specified, subsequent elements will be
appended to previous values. In ksh93, it causes subscripts to be ignored, forcing appending everything
after the last element. (Appending has different meaning due to support for multi-dimensional
arrays and nested compound datastructures.)

In Bash and Zsh, the alternate value assignment parameter expansion ( ${arr[idx]:=foo}
) evaluates the subscript twice, first to determine whether to expand the alternate, and second
to determine the index to assign the alternate to. See
evaluation
order .

$ : ${_[$(echo $RANDOM >&2)1]:=$(echo hi >&2)}
13574
hi
14485

In Zsh, arrays are indexed starting at 1 in its default mode. Emulation modes are required
in order to get any kind of portability.

Zsh and mksh do not support compound assignment arguments to typeset .

Ksh88 didn't support modern compound array assignment syntax. The original (and most portable)
way to assign multiple elements is to use the set -A name arg1 arg2 syntax. This
is supported by almost all shells that support ksh-like arrays except for Bash. Additionally,
these shells usually support an optional -s argument to set which performs
lexicographic sorting on either array elements or the positional parameters. Bash has no built-in
sorting ability other than the usual comparison operators.

Evaluation order for assignments involving arrays varies significantly depending on context.
Notably, the order of evaluating the subscript or the value first can change in almost every shell
for both expansions and arithmetic variables. See
evaluation
order for details.

Bash 4.1.* and below cannot use negative subscripts to address array indexes relative to the
highest-numbered index. You must use the subscript expansion, i.e. "${arr[@]:(-n):1}"
, to expand the nth-last element (or the next-highest indexed after n if arr[n]
is unset). In Bash 4.2, you may expand (but not assign to) a negative index. In Bash 4.3, ksh93,
and zsh, you may both assign and expand negative offsets.

ksh93 also has an additional slice notation: "${arr[n..m]}" where n
and m are arithmetic expressions. These are needed for use with multi-dimensional
arrays.

Assigning or referencing negative indexes in mksh causes wrap-around. The max index appears
to be UINT_MAX , which would be addressed by arr[-1] .

So far, Bash's -v var test doesn't support individual array subscripts. You may
supply an array name to test whether an array is defined, but can't check an element. ksh93's
-v supports both. Other shells lack a -v test.

Bugs

Fixed in 4.3 Bash 4.2.* and earlier considers each chunk of a compound assignment, including
the subscript for globbing. The subscript part is considered quoted, but any unquoted glob characters
on the right-hand side of the [ ]= will be clumped with the subscript and counted
as a glob. Therefore, you must quote anything on the right of the = sign. This is
fixed in 4.3, so that each subscript assignment statement is expanded following the same rules
as an ordinary assignment. This also works correctly in ksh93.

$ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"'
[1]=a

mksh has a similar but even worse problem in that the entire subscript is considered a glob.

$ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"'
1=a

Fixed in 4.3 In addition to the above globbing issue, assignments preceding "declare" have
an additional effect on brace and pathname expansion.

Notable quotes:

"... aname val1 val2 val3 ..."

"... aname ..."

"... type ..."

"... ad hoc ..."

So far we have seen two types of variables:
character strings and integers. The third type of variable the Korn shell supports is an
array
. As you may know, an array is like a list of things; you
can refer to specific elements in an array with integer
indices
,
so that
a[i]
refers to the
i
th element
of array
a
.

The Korn shell provides an array facility that, while useful, is much more
limited than analogous features in conventional programming languages. In particular,
arrays can be only one-dimensional (i.e., no arrays of arrays), and they are limited to
1024 elements. Indices can start at 0.

There are two ways to assign
values to elements of an array. The first is the most intuitive: you can use the standard
shell variable assignment syntax with the array index in brackets (
[]
). For example:

nicknames[2]=bob
nicknames[3]=ed

puts the values
bob
and
ed
into the elements of the array
nicknames
with indices 2 and 3, respectively. As with regular shell variables, values
assigned to array elements are treated as character strings unless the assignment is
preceded by
let
.

creates the array
aname
(if it doesn't already
exist) and assigns
val1
to
aname[0]
,
val2
to
aname[1]
, etc. As you would
guess, this is more convenient for loading up an array with an initial set of values.

To extract a value from an
array, use the syntax
${
aname
[
i
]}
. For example,
${nicknames[2]}
has the value "bob". The index
i
can be an arithmetic expression-see above.
If you use
*
in place of the index, the value will be all
elements, separated by spaces. Omitting the index is the same as specifying index 0.

Now we come to the somewhat unusual aspect of Korn shell arrays. Assume
that the only values assigned to
nicknames
are the two we saw
above. If you type
print
"
${nicknames[
*
]}"
, you will see the output:

bob ed

In other words,
nicknames[0]
and
nicknames[1]
don't exist. Furthermore, if you were to type:

nicknames[9]=pete
nicknames[31]=ralph

and then type
print
"
${nicknames[
*
]}"
, the output would look like this:

bob ed pete ralph

This is why we said "the elements of
nicknames
with indices 2 and 3" earlier, instead of "the 2nd and 3rd elements of
nicknames
". Any array elements with unassigned values just
don't exist; if you try to access their values, you will get null strings.

The shell provides an operator that tells you
how many elements an array has defined:
${#
aname
[
*
]
}
. Thus
${#nicknames[
*
]
}
has the value 4. Note that
you need the
[
*
]
because the name of the array alone is interpreted as the
0th element. This means, for example, that
${#nicknames}
equals the length of
nicknames[0]
(see
Chapter 4
). Since
nicknames[0]
doesn't exist, the value
of
${#nicknames}
is 0, the length of the null string.

To be quite frank, we feel that the Korn shell's array facility is of
little use to shell programmers. This is partially because it is so limited, but mainly
because shell programming tasks are much more often oriented toward character strings and
text than toward numbers. If you think of an array as a mapping from integers to values
(i.e., put in a number, get out a value), then you can see why arrays are
"number-dominated" data structures.

Nevertheless, we can find useful things to do with arrays.
For example,
here is a cleaner solution to Task 5-4, in which a user can select his or her terminal type
(
TERM
environment variable) at login time.
Recall that the "user-friendly" version of
this code used
select
and a
case
statement:

We can eliminate the entire
case
construct by taking advantage of the fact that the
select
construct stores the user's number choice in the
variable
REPLY
. We just need a line of code that stores all
of the possibilities for
TERM
in an array, in an order that
corresponds to the items in the
select
menu. Then we can use
$REPLY
to index the array. The resulting code is:

This code sets up the array
termnames
so that
${termnames[0]}
is "gl35a",
${termnames[1]}
is "t2000", etc. The line
TERM=${termnames[REPLY-1]}
essentially replaces the entire
case
construct by using
REPLY
to index the array.

Notice that the shell knows to interpret the text in an array index as an
arithmetic expression, as if it were enclosed in
((
and
))
, which in turn means that variable need not be preceded by
a dollar sign (
$
). We have to subtract 1 from the value of
REPLY
because array indices start at 0, while
select
menu item numbers start at 1.

The final Korn shell
feature that relates to the kinds of values that variables can hold is the
typeset
command. If you are a programmer, you might guess
that
typeset
is used to specify the
type
of a variable (integer, string, etc.); you'd be partially right.

typeset
is a rather
ad
hoc
collection of things that you can do to variables that restrict the kinds of
values they can take. Operations are specified by options to
typeset
; the basic syntax is:

typeset
-o varname
[=
value
]

Options can be combined; multiple
varname
s
can be used. If you leave out
varname
, the shell prints a
list of variables for which the given option is turned on.

The options available break down into two basic categories:

String formatting operations, such as right- and left-justification,
truncation, and letter case control.

Type and attribute functions that are of primary interest to advanced
programmers.

typeset
without options has an important meaning: if a
typeset
statement is inside a function definition, then the
variables involved all become
local
to that function (in
addition to any properties they may take on as a result of
typeset
options). The ability to define variables that are local to "subprogram"
units (procedures, functions, subroutines, etc.) is necessary for writing large
programs, because it helps keep subprograms independent of the main program and of each
other.

If you just want to declare a variable local to a function, use
typeset
without any options. For example:

Mar 13, 2017 | docstore.mik.ua

6.2 Integer Variables and Arithmetic

The expression $(($OPTIND - 1)) in the last example gives a clue as to how the shell can
do integer arithmetic. As you might guess, the shell interprets words surrounded by $(( and
)) as arithmetic expressions. Variables in arithmetic expressions do not need to be
preceded by dollar signs, though it is not wrong to do so.

Arithmetic expressions are evaluated inside double quotes, like tildes, variables, and command
substitutions. We're finally in a position to state the definitive rule about quoting strings:
When in doubt, enclose a string in single quotes, unless it contains tildes or any expression involving
a dollar sign, in which case you should use double quotes.

date (1) command on System V-derived versions of UNIX accepts arguments that tell it how
to format its output. The argument +%j tells it to print the day of the year, i.e., the number
of days since December 31st of the previous year.

We can use +%j to print a little holiday anticipation message:

print "Only $(( (365-$(date +%j)) / 7 )) weeks until the New Year!"

We'll show where this fits in the overall scheme of command-line processing in Chapter 7, Input/Output
and Command-line Processing .

The arithmetic expression feature is built in to the Korn shell's syntax, and was available in
the Bourne shell (most versions) only through the external command expr (1). Thus it is yet
another example of a desirable feature provided by an external command (i.e., a syntactic kludge)
being better integrated into the shell. [[ / ]] and getopts are also examples of this
design trend.

Korn shell arithmetic expressions are equivalent to their counterparts in the C language. [5]
Precedence and associativity are the same as in C. Table 6.2 shows the arithmetic operators that
are supported. Although some of these are (or contain) special characters, there is no need to backslash-escape
them, because they are within the $(( ... )) syntax.

[5] The assignment forms of these operators are also permitted. For example, $((x += 2))
adds 2 to x and stores the result back in x .

Table 6.2: Arithmetic Operators

Operator

Meaning

+

Plus

-

Minus

*

Times

/

Division (with truncation)

%

Remainder

<<

Bit-shift left

>>

Bit-shift right

&

Bitwise and

|

Bitwise or

~

Bitwise not

^

Bitwise exclusive or

Parentheses can be used to group subexpressions. The arithmetic expression syntax also (like C)
supports relational operators as "truth values" of 1 for true and 0 for false. Table 6.3 shows the
relational operators and the logical operators that can be used to combine relational expressions.

Table 6.3: Relational Operators

Operator

Meaning

<

Less than

>

Greater than

<=

Less than or equal

>=

Greater than or equal

==

Equal

!=

Not equal

&&

Logical and

||

Logical or

For example, $((3 > 2)) has the value 1; $(( (3 > 2) || (4 <= 1) )) also has the
value 1, since at least one of the two subexpressions is true.

The shell also supports base N numbers, where N can be up to 36. The notation
B#N means " N base B ". Of course, if you omit the B
# , the base defaults to 10.

6.2.1 Arithmetic Conditionals

Another construct, closely related to $((...)) , is ((...)) (without the leading
dollar sign). We use this for evaluating arithmetic condition tests, just as [[...]] is used
for string, file attribute, and other types of tests.

((...)) evaluates relational operators differently from $((...)) so that you can
use it in if and while constructs. Instead of producing a textual result, it just sets
its exit status according to the truth of the expression: 0 if true, 1 otherwise. So, for example,
((3 > 2)) produces exit status 0, as does (( (3 > 2) || (4 <= 1) )) , but (( (3
> 2) && (4 <= 1) )) has exit status 1 since the second subexpression isn't true.

You can also use numerical values for truth values within this construct. It's like the analogous
concept in C, which means that it's somewhat counterintuitive to non-C programmers: a value of 0
means false (i.e., returns exit status 1), and a non-0 value means true (returns exit
status 0), e.g., (( 14 )) is true. See the code for the kshdb debugger in Chapter 9
for two more examples of this.

6.2.2 Arithmetic Variables and Assignment

The (( ... )) construct can also be used to define integer variables and assign
values to them. The statement:

(( intvar=expression))

creates the integer variable intvar (if it doesn't already exist) and assigns to it the
result of expression .

That syntax isn't intuitive, so the shell provides a better equivalent: the built-in command
let . The syntax is:

let intvar=expression

It is not necessary (because it's actually redundant) to surround the expression with $((
and )) in a let statement. As with any variable assignment, there must not be any
space on either side of the equal sign ( = ). It is good practice to surround expressions
with quotes, since many characters are treated as special by the shell (e.g., * ,
# , and parentheses); furthermore, you must quote expressions that include whitespace (spaces
or TABs). See Table 6.4 for examples.

Table 6.4: Sample Integer Expression Assignments

Assignment

Value

let x=

$x

1+4

5

' 1 + 4 '

5

' (2+3) * 5 '

25

' 2 + 3 * 5 '

17

' 17 / 3 '

5

' 17 % 3 '

2

' 1<<4 '

16

' 48>>3 '

6

' 17 & 3 '

1

' 17 | 3 '

19

' 17 ^ 3 '

18

Here is a small task that makes use of integer arithmetic.

Task 6.1

Write a script called pages that, given the name of a text file, tells how many pages
of output it contains. Assume that there are 66 lines to a page but provide an option allowing
the user to override that.

We'll make our option -N , a la head . The syntax for this single option
is so simple that we need not bother with getopts . Here is the code:

Notice that we use the integer conditional (( file_lines % page_lines > 0 )) rather than
the [[ ... ]] form.

At the heart of this code is the UNIX utility wc(1) , which counts the number of lines,
words, and characters (bytes) in its input. By default, its output looks something like this:

8 34 161 bob

wc 's output means that the file bob has 8 lines, 34 words, and 161 characters.
wc recognizes the options -l , -w , and -c , which tell it to print only
the number of lines, words, or characters, respectively.

wc normally prints the name of its input file (given as argument). Since we want only the
number of lines, we have to do two things. First, we give it input from file redirection instead,
as in wc -l < bob instead of wc -l bob . This produces the number of lines preceded
by a single space (which would normally separate the filename from the number).

Unfortunately, that space complicates matters: the statement let file_lines=$(wc -l < $1)
becomes "let file_lines= N " after command substitution; the space after the equal sign
is an error. That leads to the second modification, the quotes around the command substitution expression.
The statement let file_lines="N" is perfectly legal, and let knows
how to remove the leading space.

The first if clause in the pages script checks for an option and, if it was given,
strips the dash ( - ) off and assigns it to the variable page_lines . wc in
the command substitution expression returns the number of lines in the file whose name is given as
argument.

The next group of lines calculates the number of pages and, if there is a remainder after the
division, adds 1. Finally, the appropriate message is printed.

As a bigger example of integer arithmetic, we will complete our emulation of the C shell's
pushd and popd functions (Task 4-8). Remember that these functions operate on DIRSTACK
, a stack of directories represented as a string with the directory names separated by spaces.
The C shell's pushd and popd take additional types of arguments, which are:

pushd +n takes the n th directory in the stack (starting with 0), rotates it
to the top, and cd s to it.

pushd without arguments, instead of complaining, swaps the two top directories on the
stack and cd s to the new top.

popd +n takes the n th directory in the stack and just deletes it.

The most useful of these features is the ability to get at the n th directory in the stack.
Here are the latest versions of both functions:

The argument passed to getNdirs is the n in question. The variable stackfront
is the temporary copy that will contain the first n directories when the loop is done.
stackfront starts as null; count , which counts the number of loop iterations, starts
as 0.

The first line of the loop body appends the top of the stack ( ${DIRSTACK%%*
} ) to stackfront ; the second line deletes the top from the stack. The last
line increments the counter for the next iteration. The entire loop executes N times, for
values of count from 0 to N -1.

When the loop finishes, the last directory in $stackfront is the N th directory.
The expression ${stackfront##* } extracts this directory. Furthermore,
DIRSTACK now contains the "back" of the stack, i.e., the stack without the first
n directories. With this in mind, we can now write the code for the improved versions of pushd
and popd :

These functions have grown rather large; let's look at them in turn. The if at the beginning
of pushd checks if the first argument is an option of the form +N . If so,
the first body of code is run. The first let simply strips the plus sign (+) from the argument
and assigns the result - as an integer - to the variable num . This, in turn, is passed to
the getNdirs function.

The next two assignment statements set newtop to the N th directory - i.e., the
last directory in $stackfront - and delete that directory from stackfront . The final
two lines in this part of pushd put the stack back together again in the appropriate order
and cd to the new top directory.

The elif clause tests for no argument, in which case pushd should swap the top two
directories on the stack. The first four lines of this clause assign the top two directories to
firstdir and seconddir , and delete these from the stack. Then, as above, the code
puts the stack back together in the new order and cd s to the new top directory.

The else clause corresponds to the usual case, where the user supplies a directory name
as argument.

popd works similarly. The if clause checks for the +N option, which
in this case means delete the N th directory. A let extracts the N as an integer;
the getNdirs function puts the first n directories into stackfront . Then the
line stackfront=${stackfront% *} deletes the last directory (the N th directory) from
stackfront . Finally, the stack is put back together with the N th directory missing.

The else clause covers the usual case, where the user doesn't supply an argument.

Before we leave this subject, here are a few exercises that should test your understanding of
this code:

Add code to pushd that exits with an error message if the user supplies no argument
and the stack contains fewer than two directories.

Verify that when the user specifies +N and N exceeds the number of directories
in the stack, both pushd and popd use the last directory as the N th directory.

Modify the getNdirs function so that it checks for the above condition and exits with
an appropriate error message if true.

Change getNdirs so that it uses cut (with command substitution), instead of the
while loop, to extract the first N directories. This uses less code but runs more
slowly because of the extra processes generated.

Here is a collection of coding hints that should help to get a more consistent code base.

Don't be afraid to contribute to Relax-and-Recover even if your contribution does not fully match
all this coding hints. Currently large parts of the Relax-and-Recover code are not yet in compliance
with this coding hints. This is an ongoing step by step process. Nevertheless try to understand the
idea behind this coding hints so that you know how to break them properly (i.e. "learn the rules
so you know how to break them properly").

Variables and functions must have names that explain what they do, even if it makes them longer.
Avoid too short names, in particular do not use one-letter-names (like a variable named
i - just try to 'grep' for it over the whole code to find code that is related to
i ). In general names should consist of two parts, a generic part plus a specific part
to make them meaningful. For example dev is basically meaningless because there are
so many different kind of device-like thingies. Use names like boot_dev or even better
boot_partition versus bootloader_install_device to make it unambiguous
what that thingy actually is about. Use different names for different things so that others can
'grep' over the whole code and get a correct overview what actually belongs to a particular name.

that tells the intent behind (regardless whether or not that code is the best way to do it - but
now others can easily improve it).

Use functions to structure longer programs into code blocks that can be understood independently.

Don't use || and && one-liners, write proper if-then-else-fi blocks.
Exceptions are simple do-or-die statements like COMMAND || Error "meaningful error message"
and only if it aids readability compared to a full if-then-else clause.

Do not only tell what the code does (i.e. the implementation details) but also explain what the
intent behind is (i.e. why ) to make the code maintainable.

Provide meaningful comments that tell what the computer should do and also explain why it
should do it so that others understand the intent behind so that they can properly fix issues
or adapt and enhance it as needed.

If there is a GitHub issue or another URL available for a particular piece of code provide
a comment with the GitHub issue or any other URL that tells about the reasoning behind current
implementation details.

By default bash proceeds with the next command when something failed. Do not let your code blindly
proceed in case of errors because that could make it hard to find the root cause of a failure when
it errors out somewhere later at an unrelated place with a weird error message which could lead to
false fixes that cure only a particular symptom but not the root cause.

In case of errors better abort than to blindly proceed.

At least test mandatory conditions before proceeding. If a mandatory condition is not fulfilled
abort with Error "meaningful error message" , see 'Relax-and-Recover functions' below.

Preferably in new scripts use set -ue to die from unset variables and unhandled
errors and use set -o pipefail to better notice failures in a pipeline. When leaving
the script restore the Relax-and-Recover default bash flags and options with

Implement adaptions and enhancements in a backward compatible way so that your changes do not
cause regressions for others.

One same Relax-and-Recover code must work on various different systems. On older systems as
well as on newest systems and on various different Linux distributions.

Preferably use simple generic functionality that works on any Linux system. Better very simple
code than oversophisticated (possibly fragile) constructs. In particular avoid special bash version
4 features (Relax-and-Recover code should also work with bash version 3).

When there are incompatible differences on different systems distinction of cases with separated
code is needed because it is more important that the Relax-and-Recover code works everywhere than
having generic code that sometimes fails.

When there are special issues on particular systems it is more important that the Relax-and-Recover
code works than having nice looking clean code that sometimes fails. In such special cases any dirty
hacks that intend to make it work everywhere are welcome. But for dirty hacks the above listed coding
hints become mandatory rules:

Provide explanatory comments that tell what a dirty hack does together with a GitHub issue
or any other URL that tell about the reasoning behind the dirty hack to enable others to properly
adapt or clean up a dirty hack at any time later when the reason for it had changed or gone away.

Try as good as you can to foresee possible errors or failures of a dirty hack and error out
with meaningful error messages if things go wrong to enable others to understand the reason behind
a failure.

Implement the dirty hack in a way so that it does not cause regressions for others.

For example a dirty hack like the following is perfectly acceptable:

# FIXME: Dirty hack to make it work # on "FUBAR Linux version 666" # where COMMAND sometimes inexplicably
fails # but always works after at most 3 attempts # see http://example.org/issue12345 # Retries should
have no bad effect on other systems # where the first run of COMMAND works. COMMAND || COMMAND ||
COMMAND || Error "COMMAND failed."

Use only traditional (7-bit) ASCII charactes. In particular do not use UTF-8 encoded multi-byte
characters.

Non-ASCII characters in scripts may cause arbitrary unexpected failures on systems that do
not support other locales than POSIX/C. During "rear recover" only the POSIX/C locale works (the
ReaR rescue/recovery system has no support for non-ASCII locales) and /usr/sbin/rear sets the
C locale so that non-ASCII characters are invalid in scripts. Have in mind that basically all
files in ReaR are scripts. E.g. also /usr/share/rear/conf/default.conf and /etc/rear/local.conf
are sourced (and executed) as scripts.

English documentation texts do not need non-ASCII characters. Using non-ASCII characters in
documentation texts makes it needlessly hard to display the documentation correctly for any user
on any system. When non-ASCII characters are used but the user does not have the exact right matching
locale set arbitrary nonsense can happen, cf.
https://en.opensuse.org/SDB:Plain_Text_versus_Locale

Use the available Relax-and-Recover functions when possible instead of re-implementing basic functionality
again and again. The Relax-and-Recover functions are implemented in various
lib/*-functions.sh
files .

is_true and is_false :
See
lib/global-functions.sh how to use them.
For example instead of using if [[ ! "$FOO" =~ ^[yY1] ]] ; then
use if ! is_true "$FOO" ; then

Use paired parenthesis for case patterns as in case WORD in (PATTERN) COMMANDS ;; esac
so that editor commands (like '%' in 'vi') that check for matching opening and closing parenthesis
work everywhere in the code.

A very nice tutorial by Vivek Gite (created October 31, 2008 last updated June 24, 2015).
His mistake is putting new for loop too far inside the tutorial. It should emphazied, not hidden.

Bash v4.0+ has inbuilt support for setting up a step value using {START..END..INCREMENT}
syntax:

#!/bin/bash
echo "Bash version ${BASH_VERSION}..."
for i in {0..10..2}
do
echo "Welcome $i times"
done

Sample outputs:

Bash version 4.0.33(0)-release...
Welcome 0 times
Welcome 2 times
Welcome 4 times
Welcome 6 times
Welcome 8 times
Welcome 10 times

... ... ...

Three-expression bash for loops syntax

This type of for loop share a common heritage with the C programming language. It is characterized
by a three-parameter loop control expression; consisting of an initializer (EXP1), a loop-test or
condition (EXP2), and a counting expression (EXP3).

for (( EXP1; EXP2; EXP3 ))
do
command1
command2
command3
done

A representative three-expression example in bash as follows:

#!/bin/bash
for (( c=1; c<=5; c++ ))
do
echo "Welcome $c times"
done

... ... ...

Jadu Saikia, November 2, 2008, 3:37 pm

Nice one. All the examples are explained well, thanks Vivek.

seq 1 2 20
output can also be produced using jot

jot – 1 20 2

The infinite loops as everyone knows have the following alternatives.

while(true)
or
while :

//Jadu

Andi Reinbrech, November 18, 2010, 7:42 pm

I know this is an ancient thread, but thought this trick might be helpful to someone:

For the
above example with all the cuts, simply do

set `echo $line`

This will split line into positional parameters and you can after the set simply say

F1=$1; F2=$2; F3=$3

I used this a lot many years ago on solaris with "set `date`", it neatly splits the whole date
string into variables and saves lots of messy cutting :-)

… no, you can't change the FS, if it's not space, you can't use this method

Peko, July 16, 2009, 6:11 pm

Hi Vivek,
Thanks for this a useful topic.

IMNSHO, there may be something to modify here
=======================
Latest bash version 3.0+ has inbuilt support for setting up a step value:

is there a simple way to control the number formatting? I use several computers, some
of which have non-US settings with comma as a decimal point. This means thatfor x in $(seq 0 0.1 1) gives 0 0.1 0.2 … 1 one some machines and 0 0,1 0,2 … 1 on
other.
Is there a way to force the first variant, regardless of the language settings? Can I, for example,
set the keyboard to US inside the script? Or perhaps some alternative to $x that
would convert commas to points?
(I am sending these as parameters to another code and it won't accept numbers with commas…)

The best thing I could think of is adding x=`echo $x | sed s/,/./` as a first
line inside the loop, but there should be a better solution? (Interestingly, the sed command does
not seem to be upset by me rewriting its variable.)

Thanks,
Michal

Peko July 22, 2009, 7:27 am

To Michal Kaut:

Hi Michal,

Such output format is configured through LOCALE settings.

I tried :

export LC_CTYPE="en_EN.UTF-8″; seq 0 0.1 1

and it works as desired.

You just have to find the exact value for LC_CTYPE that fits to your systems and your needs.

Peko

Peko July 22, 2009, 2:29 pm

To Michal Kaus [2]

Ooops – ;-)
Instead of LC_CTYPE,
LC_NUMERIC should be more appropriate
(Although LC_CTYPE is actually yielding to the same result – I tested both)

To Vivek:
Regarding your last example, that is : running a loop through arguments given to the script on
the command line, there is a simplier way of doing this:
# instead of:
# FILES="$@"
# for f in $FILES

# use the following syntax
for arg
do
# whatever you need here – try : echo "$arg"
done

Of course, you can use any variable name, not only "arg".

Philippe Petrinko November 11, 2009, 11:25 am

To tdurden:

Why would'nt you use

1) either a [for] loop
for old in * ; do mv ${old} ${old}.new; done

2) Either the [rename] command ?
excerpt form "man rename" :

RENAME(1) Perl Programmers Reference Guide RENAME(1)

NAME
rename – renames multiple files

SYNOPSIS
rename [ -v ] [ -n ] [ -f ] perlexpr [ files ]

DESCRIPTION
"rename" renames the filenames supplied according to the rule specified
as the first argument. The perlexpr argument is a Perl expression
which is expected to modify the $_ string in Perl for at least some of
the filenames specified. If a given filename is not modified by the
expression, it will not be renamed. If no filenames are given on the
command line, filenames will be read via standard input.

For example, to rename all files matching "*.bak" to strip the
extension, you might say

rename 's/\.bak$//' *.bak

To translate uppercase names to lower, you'd use

rename 'y/A-Z/a-z/' *

- Philippe

Philippe Petrinko November 11, 2009, 9:27 pm

If you set the shell option extglob, Bash understands some more powerful patterns. Here, a
is one or more pattern, separated by the pipe-symbol (|).

?() Matches zero or one occurrence of the given patterns
*() Matches zero or more occurrences of the given patterns
+() Matches one or more occurrences of the given patterns
@() Matches one of the given patterns
!() Matches anything except one of the given patterns

There is an interesting difference between the exit value for two different for looping structures
(hope this comes out right):for (( c=1; c<=2; c++ )) do echo -n "inside (( )) loop c is $c, "; done; echo "done (( ))
loop c is $c"
for c in {1..2}; do echo -n "inside { } loop c is $c, "; done; echo "done { } loop c is $c"
You see that the first structure does a final increment of c, the second does not. The first is
more useful IMO because if you have a conditional break in the for loop, then you can subsequently
test the value of $c to see if the for loop was broken or not; with the second structure you can't
know whether the loop was broken on the last iteration or continued to completion.

And, again, as stated many times up there, using [seq] is counter productive, because it requires
a call to an external program, when you should Keep It Short and Simple, using only bash internals
functions:

for ((c=1; c<21; c+=2)); do echo "Welcome $c times" ; done

(and I wonder why Vivek is sticking to that old solution which should be presented only for
historical reasons when there was no way of using bash internals.
By the way, this historical recall should be placed only at topic end, and not on top of the topic,
which makes newbies sticking to the not-up-to-date technique ;-) )

Sean March 9, 2010, 11:15 pm

I have a comment to add about using the builtin for (( … )) syntax. I would agree the
builtin method is cleaner, but from what I've noticed with other builtin functionality, I had
to check the speed advantage for myself. I wrote the following files:

seq_count.sh:
#!/bin/bash
for i in $(seq 1 1000000)
do
echo "Output $i"
done

And here were the results that I got:
time ./builtin_count.sh
real 0m22.122s
user 0m18.329s
sys 0m3.166s

time ./seq_count.sh
real 0m19.590s
user 0m15.326s
sys 0m2.503s

The performance increase isn't too significant, especially when you are probably going to be
doing something a little more interesting inside of the for loop, but it does show that builtin
commands are not necessarily faster.

Andi Reinbrech November 18, 2010, 8:35 pm

The reason why the external seq is faster, is because it is executed only once, and returns
a huge splurb of space separated integers which need no further processing, apart from the for
loop advancing to the next one for the variable substitution.

The internal loop is a nice and clean/readable construct, but it has a lot of overhead. The
check expression is re-evaluated on every iteration, and a variable on the interpreter's heap
gets incremented, possibly checked for overflow etc. etc.

Note that the check expression cannot be simplified or internally optimised by the interpreter
because the value may change inside the loop's body (yes, there are cases where you'd want to
do this, however rare and stupid they may seem), hence the variables are volatile and get re-evaluted.

I.e. botom line, the internal one has more overhead, the "seq" version is equivalent to either
having 1000000 integers inside the script (hard coded), or reading once from a text file with
1000000 integers with a cat. Point being that it gets executed only once and becomes static.

OK, blah blah fishpaste, past my bed time :-)

Cheers,
Andi

Anthony Thyssen June 4, 2010, 6:53 am

The {1..10} syntax is pretty useful as you can use a variable with it!

limit=10
echo {1..${limit}}
{1..10}

You need to eval it to get it to work!

limit=10
eval "echo {1..${limit}}"
1 2 3 4 5 6 7 8 9 10

'seq' is not avilable on ALL system (MacOSX for example)
and BASH is not available on all systems either.

You are better off either using the old while-expr method for computer compatiblity!

TheBonsai wrote…. "The seq-function above could use i=$((i + inc)), if only POSIX matters.
expr is obsolete for those things, even in POSIX."

I am not certain it is in Posix. It was NOT part of the original Bourne Shell, and on some
machines, I deal with Bourne Shell. Not Ksh, Bash, or anything else.

Bourne Shell syntax works everywhere! But as 'expr' is a builtin in more modern shells, then
it is not a big loss or slow down.

This is especially important if writing a replacement command, such as for "seq" where you
want your "just-paste-it-in" function to work as widely as possible.

I have been shell programming pretty well all the time since 1988, so I know what I am talking
about! Believe me.

MacOSX has in this regard been the worse, and a very big backward step in UNIX compatibility.
2 year after it came out, its shell still did not even understand most of the normal 'test' functions.
A major pain to write shells scripts that need to also work on this system.

TheBonsai June 6, 2010, 12:35 pm

Yea, the question was if it's POSIX, not if it's 100% portable (which is a difference). The
POSIX base more or less is a subset of the Korn features (88, 93), pure Bourne is something "else",
I know. Real portability, which means a program can go wherever UNIX went, only in C ;)

#!/bin/bash
# script csv01.sh
#
# 1) Usage
# This script reads from standard input
# any CSV with double-quoted data fields
# and breaks down each field on standard output
#
# 2) Within each record (line), _every_ field MUST:
# - Be surrounded by double quotes,
# - and be separated from preceeding field by a comma
# (not the first field of course, no comma before the first field)
#
while read
do
echo "New record" # this is not mandatory-just for explanation
#
#
# store REPLY and remove opening double quote
record="${REPLY#\"}"
#
#
# replace every "," by a single double quote
record=${record//\",\"/\"}
#
#
echo ${record}|while read -d \"
do
# store REPLY into variable "field"
field="${REPLY}"
#
#
echo "Field is :${field}:" # just for explanation
done
done

This script named here [cvs01.sh] must be used so:
cvs01.sh < my-cvs-file-with-doublequotes

Philippe Petrinko November 24, 2010, 6:35 pm

@Anthony,

By the way, using [REPLY] in the outer loop _and_ the inner loop is not a bug.
As long as you know what you do, this is not problem, you just have to store [REPLY] value conveniently,
as this script shows.

I didn't see this in the article or any of the comments so I thought I'd share. While this
is a contrived example, I find that nesting two groups can help squeeze a two-liner (once for
each range) into a one-liner:

Actually brace expansion happens __before__ $ parameter exapansion, so you cannot use it this
way.

Nevertheless, you could overcome this this way:

max=10; for i in $(eval echo {1..$max}); do echo $i; done

Sanya May 6, 2011, 11:42 am

Hello, Philippe

Thanks for your suggestions
You basically confirmed my findings, that bash constructions are not as simple as zsh ones.
But since I don't care about POSIX compliance, and want to keep my scripts "readable" for less
experienced people, I would prefer to stick to zsh where my simple for-loop works

Cheers, Sanya

Philippe Petrinko May 6, 2011, 12:07 pm

Sanya,

First, you got it wrong: solutions I gave are not related to POSIX, I just pointed out that
POSIX allows not to use $ in for (( )), which is just a little bit more readable – sort of.

As @Stéphane Chazelas pointed out, in bash you should use ((...)) to do arithmetic over expr
or let for legibility.

For portability, use $((...)) like @Bernhard answer.

cuonglm

+1 Even more readable! I posted my question+answer just thinking it would be helpful for my
fellow Linux users, but now I am getting a lot of benefit from the other answers :-) – Nicolas
Raoul Aug 12 '14 at 6:52

There's no reason to be using let. It's not any more standard or portable than (( a = 3 *
(2 + 1) )) (both come from ksh and are only available in ksh, bash and zsh) and it's less
legible or easy to quote. Use a=$((3 * (2 + 1))) to be portable. – Stéphane Chazelas
Aug 12 '14 at 15:27

I'm not saying it's wrong, I'm just saying it should not be used as there are better
alternatives (one for legibility ((a = 3 * (2 + 1) )), one for portability a=$((3 * (2 + 1)))),
so it's not a note against you or your answer but against it being the selected answer and
top-scorer. – Stéphane Chazelas Aug 12 '14 at 15:48

In my personal opinion, this looks a bit nicer than using expr. From man bash

Arithmetic Expansion Arithmetic expansion allows the evaluation of an arithmetic expression
and the substitution of the result. The format for arithmetic expansion is:
$((expression))

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
expansion, string expansion, command substitution, and quote removal. Arithmetic expansions
may be nested.

The evaluation is performed according to the rules listed below under ARITHMETIC EVALUATION.
If expression is invalid, bash prints a message indicating failure and no substitution occurs.

edited Dec 9 '14 at 19:39, by Stéphane Chazelas

Aside from readability, it also doesn't require forking an extra process to do the arithmetic;
it's handled by the shell itself. – chepner Aug 12 '14 at 12:02

Note that in POSIX shells, it's subject to word splitting, so it's a good habit to quote it in
list contexts.

– Stéphane Chazelas Aug 12 '14 at 15:57

Use parenthesis with quotes:

expr 3 '*' '(' 2 '+' 1 ')'

The quotes prevent bash from interpreting the parenthesis as bash syntax.

Nicolas Raoul

What Nicolas illustrates but doesn't explain is that the tokens on the expr command line must
be separated by spaces; so; for example, expr 3 "*" "(2" "+" "1)" will not work. (Also, BTW, you
probably don't need to quote the +.)

– G-Man Aug 12 '14 at 17:28

The parentheses aren't keywords like while and [[, they're syntax. If they were keywords, they
wouldn't be interpreted as such in command arguments. You need quotes so that bash doesn't parse
them but instead sees a string literal.

– Gilles Aug 13 '14 at 1:31

@Gilles: Thanks, updated! – Nicolas Raoul Aug 13 '14 at 1:55

There's no reason to be using expr for arithmetic in modern shells.

POSIX defines the $((...)) expansion operator. So you can use that in all POSIX compliant shells
(the sh of all modern Unix-likes, dash, bash, yash, mksh, zsh, posh, ksh...).a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))

ksh also introduced a let builtin which is passed the same kind of arithmetic expression, doesn't
expand into something but returns an exit status based on whether the expression resolves to 0 or
not, like in expr:if let 'a = 3 * (2 + 1)'; then
echo "$a is non-zero"
fi

However, as the quoting makes it awkward and not very legible (not to the same extent as expr of
course), ksh also introduced a ((...)) alternative form:if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then
echo "$a is non-zero and 3 > 1"
fi
((a+=2))

which is a lot more legible and should be used instead.

let and ((...)) are only available in ksh, zsh and bash. The $((...)) syntax should be preferred
if portability to other shells is needed, expr is only needed for pre-POSIX Bourne-like shells
(typically the Bourne shell or early versions of the Almquist shell).

On the non-Bourne front, there are a few shells with built-in arithmetic operator:

•as a history note, the original version of the Almquist shell, as posted on usenet in 1989 had
an expr builtin (actually merged with test), but it was removed later.

answered Aug 12 '14 at 15:41 by Stéphane Chazelas

I learn something new every day from you, Stéphane. I very much appreciate your POSIX shell
knowledge! – MattBianco Aug 21 '14 at 8:50

expr is an external command, it is not special shell syntax. Therefore, if you want expr to
see shell special characters, you need to protect them from shell parsing by quoting them.
Furthermore, expr needs each number and operator to be passed as a separate parameter. Thus:
expr 3 \* \( 2 + 1 \)

Unless you're working on an antique unix system from the 1970s or 1980s, there is very little
reason to use expr. In the old days, shells didn't have a built-in way to perform arithmetic, and
you had to call the expr utility instead. All POSIX shells have built-in arithmetic via the
arithmetic expansion syntax.
echo $((3 * (2 + 1)))

The construct $((…)) expands to the result of the arithmetic expression (written in decimal).
Bash, like most shells, supports only integer arithmetic modulo 264 (or modulo 232 for older
versions of bash and some other shells on 32-bit machines).

Bash offers an additional convenience syntax when you want to perform assignments or to test
whether an expression is 0 but don't care about the result. This construct also exists in ksh and
zsh but not in plain sh.

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then …

In addition to integer arithmetic, expr offers a few string manipulation functions. These too are
subsumed by features of POSIX shells, except for one: expr STRING : REGEXP tests whether
the string matches the specified regexp.

A POSIX shell cannot do this without external tools, but bash can with [[ STRING =~ REGEXP
]] (with a different regexp syntax - expr is a classic tool and uses BRE, bash uses ERE).

Unless you're maintaing scripts that run on 20-year-old systems, you don't need to know that
expr ever existed. Use shell arithmetic.

- Gilles

expr foo : '\(.\)' also does text extraction. bash's BASH_REMATCH achieves something similar.
It also does string comparison, which POSIX [ does not do (though one could imagine ways to use
sort for that).

Division in Bash is integer division, and it truncates the results, just
as in C:

let a=28/6
echo "a = $a" # Prints a = 4

In addition to the let command, one may use the (( ))
syntax to enforce an arithmetic context. If there is a $ (dollar sign) before the
parentheses, then a substitution is performed (more on this below). White space is allowed
inside (( )) with much greater leniency than with let, and variables inside
(( )) don't require $ (because string literals aren't allowed). Examples:

((a=$a+7)) # Add 7 to a
((a = a + 7)) # Add 7 to a. Identical to the previous command.
((a += 7)) # Add 7 to a. Identical to the previous command.
((a = RANDOM % 10 + 1)) # Choose a random number from 1 to 10.
# % is modulus, as in C.
# (( )) may also be used as a command. > or < inside (( )) means
# greater/less than, not output/input redirection.
if ((a > 5)); then echo "a is more than 5"; fi

(( )) without the leading $ is not a standard sh feature.
It comes from ksh and is only available in ksh, Bash and zsh. $(( )) substitution
is allowed in the POSIX shell. As one would expect, the result of the arithmetic expression
inside the $(( )) is substituted into the original command. Like for parameter substitution,
arithmetic substitution is subject to word splitting so should be quoted to prevent it when
in list contexts. Here are some examples of the use of the arithmetic substitution syntax:

Variables may be declared as integers so that any subsequent assignments
to them will always assume a numeric context. Essentially any variable that's declared as an
integer acts as if you had a let command in front of it when you assign to it. For
example:

This causes great confusion among people who are extracting zero-padded
numbers from various sources (although dates are by far the most common) and then doing math
on them without sanitizing them first. (It's especially bad if you write a program like this
in March, test it, roll it out... and then it doesn't blow up until August 1.)

If you have leading-zero problems with Bash's built-in arithmetic, there
are two possible solutions. The first is, obviously, to remove the leading zeroes from the
numbers before doing math with them. This is not trivial in Bash, unfortunately, because Bash
has no ability to perform substitutions on a variable using regular expressions (it can only
do it with "glob" patterns). But you could use a loop:

# This removes leading zeroes from a, one at a time.
while [[ $a = 0* ]]; do a=${a#0}; done

You can do the above without using a loop, by using extended globs; see
FAQ #67 for more information. Or, you
could use sed; that may be more efficient if you're reading many numbers from a stream,
and can arrange to sanitize them all in one command, rather than one by one.

Without a loop:

# This removes leading zeroes from a, all at once.
a=${a##+(0)}

The third solution is to force Bash to treat all numbers as base 10 by prefixing
them with 10#. This might be more efficient, but also may be less elegant to read.

a=008
let b=a+1 # Generates an error because 008 is not valid in octal.
let b=10#$a+1 # Force a to be treated as base 10. Note: the $ is required.

Finally, a note on the exit status of commands, and the notions of "true"
and "false", is in order. When bash runs a command, that command will return an exit status
from 0 to 255. 0 is considered "success" (which is "true" when used in the context of an
if or while command). However, in an arithmetic context, there are
places where the C language rules (0 is false, anything else is true) apply.

Both Bash and the Korn shell support evaluating arithmetic expressions without arithmetic expansion.
The syntax is similar to $((...)) but without the dollar sign. Because expansion
is not performed, the construct can be used without variable assignment or the colon operator:

$ x=10
$ ((x = x * 12))
$ echo $x
120
$

The real value of this construct is that it allows arithmetic expressions to be used rather than
test in if, while, and until commands. The comparison operators
set the exit status to a nonzero value if the result of the comparison is false and to a zero value
if the result is true. So writing

(( i == 100 ))

has the effect of testing i to see whether it is equal to 100 and setting the exit status
appropriately. This knowledge makes integer arithmetic ideal for inclusion in if commands:

if (( i == 100 ))
then
...
fi

The (( i == 100 )) returns an exit status of zero (true) if i equals 100 and
one (false) otherwise, and has the same effect as writing

if [ "$i" -eq 100 ]
then
...
fi

One advantage of using ((...)) rather than test is the capability to
perform arithmetic as part of the test:

if (( i / 10 != 0 ))
then
...
fi

Here the comparison returns a true if i divided by 10 is not equal to zero.

while loops can also benefit from integer arithmetic. For example,

x=0
while ((x++ < 100))
do
commands
done

executes commands 100 times. (Note that some older versions of the Korn shell and Bash do not
support the ++ and -- operators.)

Integer Types

The Korn shell and Bash both support an integer data type. You can declare variables to be integers
by using the typeset command with the -i option

typeset -i variables

where variables are any valid shell variable names. Initial values can be assigned to the variables
at the time they are declared.

Arithmetic performed on integer variables with the ((...)) construct is slightly
faster than on noninteger ones because the shell internally stores the value of an integer variable
as a binary number and not as a character string.

An integer variable cannot be assigned anything but an integer value or an integer expression.
If you attempt to assign a noninteger to it, the message bad number is printed by the Korn
shell:

$ typeset -i i
$ i=hello
ksh: i: bad number

Bash simply ignores any strings that don't contain numeric values and generates an error for anything
that contains both numbers and other characters:

The preceding example shows that integer-valued expressions can be assigned to an integer variable,
without even having to use the ((...)) construct. This holds true for both Bash
and the Korn shell.

Numbers in Different Bases

The Korn shell and Bash allow you to perform arithmetic in different bases. To write a number
in a different base with these shells, you use the notation

base#number

For example, to express the value 100 in base 8 (octal) you write

8#100

You can write constants in different bases anywhere an integer value is permitted. To assign octal
100 to the integer variable i, you can write

typeset -i i=8#100

Note that with the Korn shell the base of the first value assigned to an integer variable fixes
the base of all subsequent substitutions of that variable. In other words, if the first value you
assign to the integer variable i is an octal number, each time you subsequently substitute
the value of i on the command line, the Korn shell substitutes the value as an octal number
using the notation 8#value.

Because the first value assigned to i in this example is an octal number (8#100), all
further substitutions of i will be in octal. When the base 10 value of 50 is next assigned
to i and then i is subsequently displayed, we get the value 8#62, which is the
octal equivalent of 50 in base 10.

In the preceding example, the ((...)) construct is used to add together the
two hexadecimal values a5 and 120. The result is then displayed, once again in octal.

Bash uses both the base#number syntax for arbitrary bases and the C language syntax for
octal and hexadecimal numbers-octal numbers are preceded by 0 (zero), and hexadecimal numbers
are preceded by 0x:

If you're used to a "standard" *NIX shell you may not be familiar with bash's array feature. Although
not as powerful as similar constructs in the P languages (Perl, Python, and PHP) and others, they
are often quite useful.

Bash arrays have numbered indexes only, but they are sparse, ie you don't have to define all the
indexes. An entire array can be assigned by enclosing the array items in parenthesis:

arr=(Hello World)

Individual items can be assigned with the familiar array syntax (unless you're used to Basic or Fortran):

arr[0]=Hello
arr[1]=World

But it gets a bit ugly when you want to refer to an array item:

echo ${arr[0]} ${arr[1]}

To quote from the man page:

The braces are required to avoid conflicts with pathname expansion.

In addition the following funky constructs are available:

${arr[*]} # All of the items in the array
${!arr[*]} # All of the indexes in the array
${#arr[*]} # Number of items in the array
${#arr[0]} # Length if item zero

The ${!arr[*]} is a relatively new addition to bash, it was not part of the original array
implementation.

The following example shows some simple array usage (note the "[index]=value" assignment to assign
a specific index):

Array size: 5
Array items:
one
two
three
four
five
Array indexes:
0
1
2
3
5
Array items and indexes:
0: one
1: two
2: three
3: four
5: five

Note that the "@" sign can be used instead of the "*" in constructs such as ${arr[*]},
the result is the same except when expanding to the items of the array within a quoted string. In
this case the behavior is the same as when expanding "$*" and "$@" within quoted strings: "${arr[*]}"
returns all the items as a single word, whereas "${arr[@]}" returns each item as
a separate word.

The following example shows how unquoted, quoted "*", and quoted "@" affect the expansion (particularly
important when the array items themselves contain spaces):

Number of items in original array: 4
first item
second item
third
item
After unquoted expansion: 6
first
item
second
item
third
item
After * quoted expansion: 1
first item second item third item
After @ quoted expansion: 4
first item
second item
third
item

FAIR USE NOTICE This site contains
copyrighted material the use of which has not always been specifically
authorized by the copyright owner. We are making such material available
in our efforts to advance understanding of environmental, political,
human rights, economic, democracy, scientific, and social justice
issues, etc. We believe this constitutes a 'fair use' of any such
copyrighted material as provided for in section 107 of the US Copyright
Law. In accordance with Title 17 U.S.C. Section 107, the material on
this site is distributed without profit exclusivly for research and educational purposes. If you wish to use
copyrighted material from this site for purposes of your own that go
beyond 'fair use', you must obtain permission from the copyright owner.

ABUSE: IPs or network segments from which we detect a stream of probes might be blocked for no
less then 90 days. Multiple types of probes increase this period.

The site uses AdSense so you need to be aware of Google privacy policy. You you do not want to be
tracked by Google please disable Javascript for this site. This site is perfectly usable without
Javascript.

Original materials copyright belong
to respective owners. Quotes are made for educational purposes only
in compliance with the fair use doctrine.

FAIR USE NOTICE This site contains
copyrighted material the use of which has not always been specifically
authorized by the copyright owner. We are making such material available
to advance understanding of computer science, IT technology, economic, scientific, and social
issues. We believe this constitutes a 'fair use' of any such
copyrighted material as provided by section 107 of the US Copyright Law according to which
such material can be distributed without profit exclusively for research and educational purposes.

This is a Spartan WHYFF (We Help You For Free)
site written by people for whom English is not a native language. Grammar and spelling errors should
be expected. The site contain some broken links as it develops like a living tree...

You can use PayPal to make a contribution, supporting development
of this site and speed up access. In case softpanorama.org is down you can use the at softpanorama.info

Disclaimer:

The statements, views and opinions presented on this web page are those of the author (or
referenced source) and are
not endorsed by, nor do they necessarily reflect, the opinions of the author present and former employers, SDNP or any other organization the author may be associated with.We do not warrant the correctness
of the information provided or its fitness for any purpose.