May the source be with you, but remember the KISS principle ;-)

Contents Bulletin Scripting in shell and Perl Network troubleshooting History Humor

Arithmetic Expressions in BASH


See also

Recommended  Links

Quoting caveats Double square bracket conditionals Single square bracket conditionals
Compound comparisons if statements in shell  Macro substitutions caveats Case statement in shell Bash Built-in Variables Array Variables
BASH Debugging Annotated List of Bash Enhancements bash Tips and Tricks Tips and Tricks Humor Etc


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).

All-in-all despite warts ((..)) 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 loop and case statement. for example:
function mess
   if (( "$1" > 0 )) ; then
   tail -$total /var/adm/messages | more

New  ((...)) conditional expression  should always be used instead of expr function

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

 (( percent >= 0 && percent <= 100 ))

Another  example:

 (( uid > 10  &&  uid < 100  )) # arithmetic condition

As long as you can limit yourself to integer arithmetic, you can use all the standard C-language operators inside of  ((...)) conditionals.

As long as you can limit yourself to integer arithmetic, you can use all the standard C-language operators inside of  ((...)) conditionals.

Here is a more complex examples, that demonstrates that it not always possible to get rid of leading $ in arithmetic expressions -- parameters still should contain it:

function mess
   if (( "$1" > 0 )) ; then
   tail -$total /var/adm/messages | more

Arithmetic operations

All arithmetic operations are performed  on integers. Syntax is similar to conditional expressions with some twists. For example:

i=$(( i + 1 ))
(( i=i+1 ))
(( i++ ))
let i+=1
let i++
i=$(( i++))
let i++

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).

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


If you use let ( and I do not recomment using let for anything but C-style shorcut operators like += and ++) it is prudent to put expression in double quote to avoid conflict with bash metasymbols such as "*", '('  and ')'.

If you use let ( and I do not recomment using let for anything but C-style shorcut operators like += and ++) it is prudent to put expression in double quote to avoid conflict with bash metasymbols such as "*", '('  and ')'.

Shortcuts for assignment operators in bash

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

let i=$i+5

can be written as

let i+=5
Usual C-style increments/decrements  ++/--  are available  too:
let $i++
(( $i++ ))

Here is an extended table of available shortcuts:

Operator Operation with assignment Use Meaning
= Simple assignment a=b a=b
*= Multiplication a*=b a=(a*b)
/= Division a/=b a=(a/b)
%= Remainder a%=b a=(a%b)
+= Addition a+=b a=(a+b)
-= Subtraction a-=b a=(a-b)
<<= Bit-shift left a<<=b a=(a<<b)
>>= Bit-shift right a>>=b a=(a>>b)
&= Bitwise "and" a&=b a=(a&b)
^= Bitwise "exclusive or" a^=b a=(a^b)
|= Bitwise "or" a|=b a=(a|b)

The differences between the ((...)) command and let command

While constructs are almost identical there some minor differences. I would prefer to use ((..)) in both conditional expressions  and assignment statements as let construct contains a couple of unpleasant gotchas:

Ternary operator ?

In C ?: is a ternary operator that provides capabilities for a basic conditional expression. It is commonly referred to as the conditional operator, inline if (iif), or ternary if.

A traditional if-else construct in C  is written:

if (a >= b) {
    max = a;
} else {
    max = b;

This can be rewritten as the following statement:

max = a >= b ? a : b;

Although many ternary operators are possible, only conditional operator is common. Other ternary operators so rare, that the conditional operator is commonly referred to as the ternary operator.

It is now re-implemented and is available in bash. The example above can be written as

let "max = a >= b ? a : b" # note usage of double quotes
or better
 (( max = a >= b ? a : b )) 

For example:

let "max = a >= b ? a : b"   # Condition is true
echo $max

let "b += 2"
let "max = a > b ? a : b"   # Condition is false
echo $max 
Here is a relevant discussion in Stack Overflow

Q: Is there a way to do something like this

int a = (b == 5) ? c : d;

using Bash?

Daniel Daranas asked Oct 17 '10 at 14:38 

A: Code:

   a=$(( b == 5 ? c : d )) 
answered Oct 17 '10 at 14:53  by Vladimir

A: ternary operator ? : is just short form of if/else

case "$b" in 
   5) a=$c ;; 
   *) a=$d ;; 


a="$d"; [[ $b = 5 ]] && a="$c" 

down vote for [ $b == 5 ] && a=$c || a=$d

this solution is incorrect !!! Watch out with this construct, as the part after the || is also executed when the part between && and || fails. Use

[ $b == 5 ] && { a=$c; true; } || a=$d – James Andino Nov 22 '13 at 23:14

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:

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

(ping -c1 localhost&>/dev/null) && { echo "true", true; } || { echo "false"; }
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 variable has string value you might have a problem.

Generally in shells line bash or ksh93 spaces are treated as in any normal algorithmic language: they are optional.  But this is not always the case. Spaces before and after equal sign have a special meaning. 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:

i=$(( i + $2 ))

Additional examples

Instant integer calculator using the echo command. for example for conversion of megabytes in kilobytes and bytes

echo $(( 33*1024 ))

Additional examples from Internal Commands and Builtins

let a=11            # Same as 'a=11'
let a=a+5           # Equivalent to  let "a = a + 5"
                    # (Double quotes and spaces make it more readable.)
echo "sum of 11 + 5 is $a"  # 16

let "a <<= 3"       # Equivalent to  let "a = a << 3"
echo "\"\$a\" (=16) left-shifted 3 places = $a"
                    # 128

let "a /= 4"        # Equivalent to  let "a = a / 4"
echo "128 / 4 = $a" # 32

let "a -= 5"        # Equivalent to  let "a = a - 5"
echo "32 - 5 = $a"  # 27

let "a *=  10"      # Equivalent to  let "a = a * 10"
echo "27 * 10 = $a" # 270

let "a %= 8"        # Equivalent to  let "a = a % 8"
echo "270 modulo 8 = $a  (270 / 8 = 33, remainder $a)"
                    # 6
C-style shortcuts
let a++             # C-style (post) increment.
let a--             # C-style decrement.
# Of course, ++a, etc., also allowed . . .



The man page for bash v3.2.48 says:

[...] The format for arithmetic expansion is: $((expression))

The old format $[expression] is deprecated and will be removed in upcoming versions of bash. So $[...] is old syntax that should not be used anymore.

The feature has been deprecated since bash-2.0, but persists till today (bash-4.2).

History of this feature is explained in the old email from the  bug-bash mailing list. 

“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.”

Dr. Nikolai Bezroukov

Top updates

Softpanorama Switchboard
Softpanorama Search


Old News ;-)

Please visit Heiner Steven's SHELLdorado, the best shell scripting site on the Internet

[Mar 13, 2017] 6.3 Arrays

Notable quotes:
"... aname val1 val2 val3 ..."
"... aname ..."
"... type ..."
"... ad hoc ..."
Mar 13, 2017 | name="KSH-CH-6-SECT-3">

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:


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 .

The second way to assign values to an array is with a variant of the set statement, which we saw in Chapter 3, Customizing Your Environment . The statement:

set -A 

aname val1 val2 val3


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:


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.

You can preserve whatever whitespace you put in your array elements by using " $ { aname [@] } " (with the double quotes) instead of $ { aname [ * ] } " , just as you can with " $@ " instead of $ * .

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:

print 'Select your terminal type:'
PS3='terminal? '
select term in
    'Givalt GL35a' \
    'Tsoris T-2000' \
    'Shande 531' \
    'Vey VT99'
    case $REPLY in
        1 ) TERM=gl35a ;;
        2 ) TERM=t2000 ;;
        3 ) TERM=s531 ;;
        4 ) TERM=vt99 ;;
        * ) print "invalid." ;;
    if [[ -n $term ]]; then
        print "TERM is $TERM"

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:

set -A termnames gl35a t2000 s531 vt99
print 'Select your terminal type:'
PS3='terminal? '
select term in
    'Givalt GL35a' \
    'Tsoris T-2000' \
    'Shande 531' \
    'Vey VT99'
    if [[ -n $term ]]; then
        print "TERM is $TERM"

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.

6.3.1 typeset

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:


-o varname




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:

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

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

6.3.2 Local Variables in Functions

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:

function afunc {
    typeset diffvar
    print "samevar is $samevar"
    print "diffvar is $diffvar"

print "samevar is $samevar"
print "diffvar is $diffvar"
print "samevar is $samevar"
print "diffvar is $diffvar"

This code will print the following:

samevar is globvalue
diffvar is globvalue
samevar is funcvalue
diffvar is funcvalue
samevar is funcvalue
diffvar is globvalue

Figure 6.1 shows this graphically.

Figure 6.1: Local variables in functions

[Mar 13, 2017] Leaning the Korn shell: Chapter 6 Integer Variables and Arithmetic

Mar 13, 2017 |
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:

if [[ $1 = -+([0-9]) ]]; then
    let page_lines=${1#-}
    let page_lines=66
let file_lines="$(wc -l < $1)"

let pages=file_lines/page_lines
if (( file_lines % page_lines > 0 )); then
    let pages=pages+1

print "$1 has $pages pages of text."

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:

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:

function pushd { # push current directory onto stack
    if [[ -d $dirname && -x $dirname ]]; then
  	  cd $dirname
        DIRSTACK="$dirname ${DIRSTACK:-$PWD}"
        print "$DIRSTACK"
        print "still in $PWD."

function popd {  # pop directory off the stack, cd to new top
    if [[ -n $DIRSTACK ]]; then
        cd ${DIRSTACK%% *}
        print "$PWD"
        print "stack empty, still in $PWD."

To get at the n th directory, we use a while loop that transfers the top directory to a temporary copy of the stack n times. We'll put the loop into a function called getNdirs that looks like this:

function getNdirs{
    let count=0
    while (( count < $1 )); do
        stackfront="$stackfront ${DIRSTACK%% *}"
        let count=count+1

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 :

function pushd {
    if [[ $1 = ++([0-9]) ]]; then
        # case of pushd +n: rotate n-th directory to top
        let num=${1#+}
        getNdirs $num

        newtop=${stackfront##* }

        DIRSTACK="$newtop $stackfront $DIRSTACK"
        cd $newtop

    elif [[ -z $1 ]]; then
        # case of pushd without args; swap top two directories
        firstdir=${DIRSTACK%% *}
        seconddir=${DIRSTACK%% *}
        DIRSTACK=${DIRSTACK#* } 
        DIRSTACK="$seconddir $firstdir $DIRSTACK"
        cd $seconddir

  	  cd $dirname
        # normal case of pushd dirname
        if [[ -d $dirname && -x $dirname ]]; then
            DIRSTACK="$dirname ${DIRSTACK:-$PWD}"
            print "$DIRSTACK"
            print still in "$PWD."

function popd {      # pop directory off the stack, cd to new top
    if [[ $1 = ++([0-9]) ]]; then
        # case of popd +n: delete n-th directory from stack
        let num={$1#+}
        getNdirs $num
        stackfront=${stackfront% *}
        DIRSTACK="$stackfront $DIRSTACK"

        # normal case of popd without argument
        if [[ -n $DIRSTACK ]]; then
            DIRSTACK=${DIRSTACK#* }
            cd ${DIRSTACK%% *}
            print "$PWD"
            print "stack empty, still in $PWD."

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:

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

  2. 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.

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

  4. 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.

[Nov 04, 2016] Coding Style rear-rear Wiki

Reading rear sources is an interesting exercise. It really demonstrates attempt to use "reasonable' style of shell programming and you can learn a lot.
Nov 04, 2016 |

Relax-and-Recover is written in Bash (at least bash version 3 is needed), a language that can be used in many styles. We want to make it easier for everybody to understand the Relax-and-Recover code and subsequently to contribute fixes and enhancements.

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").

The overall idea behind this coding hints is:

Make yourself understood

Make yourself understood to enable others to fix and enhance your code properly as needed.

From this overall idea the following coding hints are derived.

For the fun of it an extreme example what coding style should be avoided:

#!/bin/bash for i in `seq 1 2 $((2*$1-1))`;do echo $((j+=i));done


Try to find out what that code is about - it does a useful thing.

Code must be easy to read Code should be easy to understand

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.

Here the initial example so that one can understand what it is about:

#!/bin/bash # output the first N square numbers # by summing up the first N odd numbers 1 3 ... 2*N-1 # where each nth partial sum is the nth square number # see # this way it is a little bit faster for big N compared to # calculating each square number on its own via multiplication N=$1 if ! [[ $N =~ ^[0-9]+$ ]] ; then echo "Input must be non-negative integer." 1>&2 exit 1 fi square_number=0 for odd_number in $( seq 1 2 $(( 2 * N - 1 )) ) ; do (( square_number += odd_number )) && echo $square_number done

Now the intent behind is clear and now others can easily decide if that code is really the best way to do it and easily improve it if needed.

Try to care about possible errors

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.

Maintain Backward Compatibility

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

Dirty hacks welcome

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:

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 # Retries should have no bad effect on other systems # where the first run of COMMAND works. COMMAND || COMMAND || COMMAND || Error "COMMAND failed."

Character Encoding

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

Text Layout Variables Functions Relax-and-Recover functions

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/* files .

test, [, [[, (( Paired parenthesis See also

[Dec 06, 2015] Bash For Loop Examples

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.
June 24, 2015 |

... ... ...

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

echo "Bash version ${BASH_VERSION}..."
for i in {0..10..2}
     echo "Welcome $i times"

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 ))

A representative three-expression example in bash as follows:

for (( c=1; c<=5; c++ ))
   echo "Welcome $c times"
... ... ...

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 :


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:

for i in {1..5}
1) The increment feature seems to belong to the version 4 of bash.
Accordingly, my bash v3.2 does not include this feature.

BTW, where did you read that it was 3.0+ ?
(I ask because you may know some good website of interest on the subject).

2) The syntax is {} where from, to, step are 3 integers.
You code is missing the increment.

Note that GNU Bash documentation may be bugged at this time,
because on GNU Bash manual, you will find the syntax {x..y[incr]}
which may be a typo. (missing the second ".." between y and increment).


The Bash Hackers page
again, see
seeems to be more accurate,
but who knows ? Anyway, at least one of them may be right… ;-)

Keep on the good work of your own,
Thanks a million.

- Peko

Michal Kaut July 22, 2009, 6:12 am

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 that
for 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.)


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 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)

By the way, Vivek has already documented the matter :

Philippe Petrinko October 30, 2009, 8:35 am

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
# whatever you need here – try : echo "$arg"

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)

rename – renames multiple files

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

"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


Philippe Petrinko November 12, 2009, 3:44 pm

To Sean:
Right, the more sharp a knife is, the easier it can cut your fingers…

I mean: There are side-effects to the use of file globbing (like in [ for f in * ] ) , when the globbing expression matches nothing: the globbing expression is not susbtitued.

Then you might want to consider using [ nullglob ] shell extension,
to prevent this.

Devil hides in detail ;-)

Dominic January 14, 2010, 10:04 am

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.

Dominic January 14, 2010, 10:09 am

sorry, my previous post would have been clearer if I had shown the output of my code snippet, which is:
inside (( )) loop c is 1, inside (( )) loop c is 2, done (( )) loop c is 3
inside { } loop c is 1, inside { } loop c is 2, done { } loop c is 2

Philippe Petrinko March 9, 2010, 2:34 pm


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:

for ((i=1;i<=1000000;i++))
echo "Output $i"

for i in $(seq 1 1000000)
echo "Output $i"

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

time ./
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 :-)


Anthony Thyssen June 4, 2010, 6:53 am

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

echo {1..${limit}}

You need to eval it to get it to work!

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!

   limit=10; n=1;
   while [ $n -le 10 ]; do
     echo $n;
     n=`expr $n + 1`;

Alternativally use a seq() function replacement…

 # seq_count 10
seq_count() {
  i=1; while [ $i -le $1 ]; do echo $i; i=`expr $i + 1`; done
# simple_seq 1 2 10
simple_seq() {
  i=$1; while [ $i -le $3 ]; do echo $i; i=`expr $i + $2`; done
seq_integer() {
    if [ "X$1" = "X-f" ]
    then format="$2"; shift; shift
    else format="%d"
    case $# in
    1) i=1 inc=1 end=$1 ;;
    2) i=$1 inc=1 end=$2 ;;
    *) i=$1 inc=$2 end=$3 ;;
    while [ $i -le $end ]; do
      printf "$format\n" $i;
      i=`expr $i + $inc`;

Edited: by Admin – added code tags.

TheBonsai June 4, 2010, 9:57 am

The Bash C-style for loop was taken from KSH93, thus I guess it's at least portable towards Korn and Z.

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

Philippe Petrinko June 4, 2010, 10:15 am

Right Bonsai,
( )

But FOR C-style does not seem to be POSIXLY-correct…

Read on-line reference issue 6/2004,
Top is here,

and the Shell and Utilities volume (XCU) T.OC. is here
doc is:

and FOR command:

Anthony Thyssen June 6, 2010, 7:18 am

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 ;)

Philippe Petrinko November 22, 2010, 8:23 am

And if you want to get rid of double-quotes, use:

one-liner code:
while read; do record=${REPLY}; echo ${record}|while read -d ","; do field="${REPLY#\"}"; field="${field%\"}"; echo ${field}; done; done<data

script code, added of some text to better see record and field breakdown:

while read
echo "New record"
echo ${record}|while read -d ,
echo "Field is :${field}:"

Does it work with your data?

- PP

Philippe Petrinko November 22, 2010, 9:01 am

Of course, all the above code was assuming that your CSV file is named "data".

If you want to use anyname with the script, replace:




And then use your script file (named for instance "myScript") with standard input redirection:

myScript < anyFileNameYouWant


Philippe Petrinko November 22, 2010, 11:28 am

well no there is a bug, last field of each record is not read – it needs a workout and may be IFS modification ! After all that's what it was built for… :O)

Anthony Thyssen November 22, 2010, 11:31 pm

Another bug is the inner loop is a pipeline, so you can't assign variables for use later in the script. but you can use '<<<' to break the pipeline and avoid the echo.

But this does not help when you have commas within the quotes! Which is why you needed quotes in the first place.

In any case It is a little off topic. Perhaps a new thread for reading CVS files in shell should be created.

Philippe Petrinko November 24, 2010, 6:29 pm

Would you try this one-liner script on your CSV file?

This one-liner assumes that CSV file named [data] has __every__ field double-quoted.

while read; do r="${REPLY#\"}";echo "${r//\",\"/\"}"|while read -d \";do echo "Field is :${REPLY}:";done;done<data

Here is the same code, but for a script file, not a one-liner tweak.

# script
# 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
echo "New record" # this is not mandatory-just for explanation
# store REPLY and remove opening double quote
# replace every "," by a single double quote
echo ${record}|while read -d \"
# store REPLY into variable "field"
echo "Field is :${field}:" # just for explanation

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

Philippe Petrinko November 24, 2010, 6:35 pm


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.

TheBonsai March 8, 2011, 6:26 am
for ((i=1; i<=20; i++)); do printf "%02d\n" "$i"; done

nixCraft March 8, 2011, 6:37 am

+1 for printf due to portability, but you can use bashy .. syntax too

for i in {01..20}; do echo "$i"; done

TheBonsai March 8, 2011, 6:48 am

Well, it isn't portable per se, it makes it portable to pre-4 Bash versions.

I think a more or less "portable" (in terms of POSIX, at least) code would be

while [ "$((i >= 20))" -eq 0 ]; do
  printf "%02d\n" "$i"

Philip Ratzsch April 20, 2011, 5:53 am

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:

for num in {{1..10},{15..20}};do echo $num;done

Great reference article!

Philippe Petrinko April 20, 2011, 8:23 am

Nice thing to think of, using brace nesting, thanks for sharing.

Philippe Petrinko May 6, 2011, 10:13 am

Hello Sanya,

That would be because brace expansion does not support variables. I have to check this.
Anyway, Keep It Short and Simple: (KISS) here is a simple solution I already gave above:

for (( x = $xstart; x <= $xend; x += $xstep)); do echo $x;done

Actually, POSIX compliance allows to forget $ in for quotes, as said before, you could also write:

for (( x = xstart; x <= xend; x += xstep)); do echo $x;done

Philippe Petrinko May 6, 2011, 10:48 am


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


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.

Second, why do you see this less readable than your [zsh] [for loop]?

for (( x = start; x <= end; x += step)) do
echo "Loop number ${x}"

It is clear that it is a loop, loop increments and limits are clear.

IMNSHO, if anyone cannot read this right, he should not be allowed to code. :-D


Anthony Thyssen May 8, 2011, 11:30 pm

If you are going to do… $(eval echo {1..$max});
You may as well use "seq" or one of the many other forms.
See all the other comments on doing for loops.

Tom P May 19, 2011, 12:16 pm

I am trying to use the variable I set in the for line on to set another variable with a different extension. Couldn't get this to work and couldnt find it anywhere on the web… Can someone help.


FILE_TOKEN=`cat /tmp/All_Tokens.txt`
for token in $FILE_TOKEN
A1_$token=`grep $A1_token /file/path/file.txt | cut -d ":" -f2`

my goal is to take the values from the ALL Tokens file and set a new variable with A1_ infront of it… This tells be that A1_ is not a command…

[May 02, 2015] shell - Parenthesis in bash arithmetic 3 (2 + 1)

Unix & Linux Stack Exchange

Parenthesis in bash arithmetic: 3 * (2 + 1)

expr does not seem to like parenthesis (used in mathematics to explicit operator priority):
expr 3 * (2 + 1)
bash: syntax error near unexpected token `('

How to express operator priority in bash?

/ bash / shell / quoting / arithmetic

asked Aug 12 '14 at 6:09 by Nicolas Raoul

Another way to use let bash builtin:
$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"


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.


+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

@StéphaneChazelas: Updated my answer! – cuonglm Aug 12 '14 at 16:19

You can use the arithmetic expansion instead.
echo "$(( 3 * ( 2 + 1 ) ))"

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:

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) ))

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"

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"

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:

•csh/tcsh (actually the first Unix shell with arithmetic evaluation built-in):
@ a = 3 * (2 + 1)

•akanga (based on rc)
a = $:'3 * (2 + 1)'

•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).

– Stéphane Chazelas Aug 15 '14 at 11:25

[May 02, 2015] ArithmeticExpression

2014-02-13 | Greg's Wiki

Arithmetic in BASH is integer math only. You can't do floating point math in Bash; if you need that capability, see Bash FAQ #22.

Also see the Bash hackers article about the full syntax theory. /!\ The $[ ] syntax is deprecated

There are several ways to tell Bash to treat numbers as integers instead of strings, and to do basic arithmetic operations on them. The first is to use the let command:

let a=17+23
echo "a = $a"      # Prints a = 40

Note that each arithmetic expression has to be passed as a single argument to the let command, so you need quotes if there are spaces or globbing characters, thus:

let a=17 + 23      # WRONG
let a="17 + 23"    # Right
let 'a = 17 + 23'  # Right
let a=17 a+=23     # Right (2 arithmetic expressions)

let a[1]=1+1       # Wrong (try after touch a1=1+1 or with shopt -s failglob)
let 'a[1]=1+1'     # Right
let a\[1]=1+1      # Right

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:

a=$((a+7))         # POSIX-compatible version of previous code.
if test "$((a%4))" = 0; then ...
lvcreate -L "$((4*1096))" -n lvname vgname   # Actual HP-UX example.

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:

unset b             # Forget any previous declarations
b=7+5; echo "$b"    # Prints 7+5
declare -i b        # Declare b as an integer
b=7+5; echo "$b"    # Prints 12

Also, array indices are a numeric context:

while read line; do
   array[n++]=$line      # array[] forces a numeric context

There is one common pitfall with arithmetic expressions in Bash: numbers with leading zeroes are treated as octal. For example,

# Suppose today is September 19th.
month=$(date +%m)
next_month=$(( (month == 12) ? 1 : month+1 ))
# bash: 09: value too great for base (error token is "09")

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.

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.

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.

Some examples:

true; echo "$?"       # Writes 0, because a successful command returns 0.
((10 > 6)); echo "$?" # Also 0.  An arithmetic command returns 0 for true.
echo "$((10 > 6))"    # Writes 1.  An arithmetic expression returns 1 for true.

In addition to a comparison returning 1 for true, an arithmetic expression that evaluates to a non-zero value is also true in the sense of a command.

if ((1)); then echo true; fi     # Writes true.

This also lets you use "flag" variables, just like in a C program:

while ...; do
  if something; then found=1; fi    # Found one!  Keep going.
if ((found)); then ...

Here is a function to convert numbers in other bases to decimal (base 10):

todec() {
    echo "$(( $1#$2 ))"


todec 16 ffe    # -> 4094
todec 2 100100  # -> 36

[Nov 21, 2010] Integer Arithmetic

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

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 ))

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 ]

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

if (( i / 10 != 0 ))

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,

while ((x++ < 100))

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:

$ typeset -i i
$ i=hello
$ echo $i
$ i=1hello
bash: 1hello: value too great for base (error token is "1hello")
$ i=10+15
$ echo $i

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


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


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.

$ typeset -i i=8#100
$ echo $i
$ i=50
$ echo $i
$ (( i = 16#a5 + 16#120 ))
$ echo $i

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:

$ typeset -i i=0100
$ echo $i
$ i=0x80
$ echo $i
$ i=2#1101001
$ echo $i
$ (( i = 16#a5 + 16#120 ))
$ echo $i

Unlike the Korn shell, Bash doesn't keep track of the variable's base; integer variables are displayed as decimal numbers. You can always use printf to print integers in octal or hexadecimal format.

[Nov 21, 2010] Advanced Bash Shell Scripting Guide - Operators

Arithmetic operators often occur in an expr or let expression.

Example 8-2. Using Arithmetic Operations

# Counting to 11 in 10 different ways.

n=1; echo -n "$n "

let "n = $n + 1"   # let "n = n + 1"  also works.
echo -n "$n "

: $((n = $n + 1))
#  ":" necessary because otherwise Bash attempts
#+ to interpret "$((n = $n + 1))" as a command.
echo -n "$n "

(( n = n + 1 ))
#  A simpler alternative to the method above.
#  Thanks, David Lombard, for pointing this out.
echo -n "$n "

n=$(($n + 1))
echo -n "$n "

: $[ n = $n + 1 ]
#  ":" necessary because otherwise Bash attempts
#+ to interpret "$[ n = $n + 1 ]" as a command.
#  Works even if "n" was initialized as a string.
echo -n "$n "

n=$[ $n + 1 ]
#  Works even if "n" was initialized as a string.
#* Avoid this type of construct, since it is obsolete and nonportable.
#  Thanks, Stephane Chazelas.
echo -n "$n "

# Now for C-style increment operators.
# Thanks, Frank Wang, for pointing this out.

let "n++"          # let "++n"  also works.
echo -n "$n "

(( n++ ))          # (( ++n )  also works.
echo -n "$n "

: $(( n++ ))       # : $(( ++n )) also works.
echo -n "$n "

: $[ n++ ]         # : $[ ++n ]] also works
echo -n "$n "


exit 0
Integer variables in Bash are actually signed long (32-bit) integers, in the range of -2147483648 to 2147483647. An operation that takes a variable outside these limits will give an erroneous result.
echo "a = $a"      # a = 2147483646
let "a+=1"         # Increment "a".
echo "a = $a"      # a = 2147483647
let "a+=1"         # increment "a" again, past the limit.
echo "a = $a"      # a = -2147483648
                   #      ERROR (out of range)

As of version 2.05b, Bash supports 64-bit integers. Bash does not understand floating point arithmetic. It treats numbers containing a decimal point as strings.


let "b = $a + 1.3"  # Error.
# let: b = 1.5 + 1.3: syntax error in expression (error token is ".5 + 1.3")

echo "b = $b"       # b=1
Use bc in scripts that that need floating point calculations or math library functions.

[Jun 19, 2008] Bash Arrays by Mitch Frazier

Jun, 2008 | Linux Journal

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):
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=(one two three four [5]=five)

echo "Array size: ${#array[*]}"

echo "Array items:"
for item in ${array[*]}
    printf "   %s\n" $item

echo "Array indexes:"
for index in ${!array[*]}
    printf "   %d\n" $index

echo "Array items and indexes:"
for index in ${!array[*]}
    printf "%4d: %s\n" $index ${array[$index]}
Running it produces the following output:
Array size: 5
Array items:
Array indexes:
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):


array=("first item" "second item" "third" "item")

echo "Number of items in original array: ${#array[*]}"
for ix in ${!array[*]}
    printf "   %s\n" "${array[$ix]}"

echo "After unquoted expansion: ${#arr[*]}"
for ix in ${!arr[*]}
    printf "   %s\n" "${arr[$ix]}"

echo "After * quoted expansion: ${#arr[*]}"
for ix in ${!arr[*]}
    printf "   %s\n" "${arr[$ix]}"

echo "After @ quoted expansion: ${#arr[*]}"
for ix in ${!arr[*]}
    printf "   %s\n" "${arr[$ix]}"
When run it outputs:
Number of items in original array: 4
   first item
   second item

After unquoted expansion: 6

After * quoted expansion: 1
   first item second item third item

After @ quoted expansion: 4
   first item
   second item

Mitch Frazier is the System Administrator at Linux Journal.

Arithmetic expansion with double parentheses, and using let

The use of backticks (backquotes) in arithmetic expansion has been superseded by double parentheses -- ((...)) and $((...)) -- and also by the very convenient let construction.
z=$((z+3))                                  #  Also correct.
                                            #  Within double parentheses,
                                            #+ parameter dereferencing
                                            #+ is optional.

# $((EXPRESSION)) is arithmetic expansion.  #  Not to be confused with
                                            #+ command substitution.

# You may also use operations within double parentheses without assignment.

  echo "n = $n"                             # n = 0

  (( n += 1 ))                              # Increment.
# (( $n += 1 )) is incorrect!
  echo "n = $n"                             # n = 1

let z=z+3
let "z += 3"  #  Quotes permit the use of spaces in variable assignment.
              #  The 'let' operator actually performs arithmetic evaluation,
              #+ rather than expansion.

Recommended Links

Softpanorama hot topic of the month

Softpanorama Recommended

Please visit  Heiner Steven SHELLdorado  the best shell scripting site on the Internet

Advanced Bash-Scripting Guide

Bash Arrays  by Mitch Frazier (Linux Journal, 2008) Turn Vim into a bash IDE


Linux tip Bash test and comparison functions

Common shell script mistakes


Examples shipped with bash 3.2 and newer
Path Description X-ref
Deprecated sample implementation of a bash debugger.  
Shell completion code.  
Example functions.  
Various array functions (ashift, array_sort, reverse).  
Convert an array to a string.  
An almost ksh-compatible 'autoload' (no lazy load). ksh
An almost ksh-compatible 'autoload' (no lazy load). ksh
A more ksh-compatible 'autoload' (with lazy load). ksh
A replacement for basename(1). basename
Fast basename(1) and dirname(1) functions for bash/sh. basename, dirname
Start, control, and end co-processes.  
Control shell co-processes (see coprocess.bash).  
README for coshell and coproc.  
A C-shell compatibility package. csh
Directory manipulation functions from the book The Korn Shell.  
A replacement for dirname(1). dirname
Find out if a directory is empty.  
Display the exit status of processes.  
Like command, but forces the use of external command.  
Recursive factorial function.  
Front-end to sync TERM changes to both stty(1) and readline 'bind'. stty.bash
Print out definitions for functions named by arguments.  
Get a web page from a remote server (wget(1) in bash).  
getopt function that parses long-named options.  
Internet address conversion (inet2hex and hex2inet).  
Return zero if the argument is in the path and executable. inpath
Test user input on numeric or character value.  
Test user input on numeric values, with floating point.  
Test user input for valid IP addresses.  
Julian date conversion.  
Look for running jobs.  
Try to keep some programs in the foreground and running.  
ksh-like cd: cd [-LP] [dir[change]]. ksh
ksh-like arithmetic test replacements. ksh
Functions and aliases to provide the beginnings of a ksh environment for bash ksh
Replace the login and newgrp built-ins in old Bourne shells.  
Rename files to lowercase. rename lower
Find and print a manpage. fman
Print MH folders, useful only because folders(1) doesn't print mod date/times.  
Notify when jobs change status.  
Path related functions (no_path, add_path, pre-path, del_path). path
Recursive directory traverser.  
A clone of the C shell built-in repeat. repeat, csh
A clone of the C shell built-in repeat. repeat, csh
Generate a sequence from m to n;m defaults to 1.  
Generate a sequence from m to n;m defaults to 1.  
Readline-based pager. cat, readline pager
Readline-based pagers. cat, readline pager
Sort the positional parameters.  
A function to emulate the ancient ksh built-in. ksh
A function to emulate the ancient ksh built-in. ksh
A shell function to set the terminal type interactively or not.  
An implementation of the 10th Edition Unix sh built-in whatis(1) command.  
An almost ksh-compatible whence(1) command.  
An emulation of which(1) as it appears in FreeBSD.  
Convert csh alias commands to bash functions. csh, aliasconv
A find(1) clone.  
Example loadable replacements.  
Return nondirectory portion of pathname. basename
cat(1) replacement with no options—the way cat was intended. cat, readline pager
cut(1) replacement.  
Return directory portion of pathname. dirname
Print file info.  
POSIX.2 getconf utility.
Replacement definitions for ones the system doesn't provide.  
Copy first part of files.  
Obligatory "Hello World" / sample loadable.  
POSIX.2 user identity.  
Make links.  
Print login name of current user.  
Simple makefile for the sample loadable built-ins.  
Make directories.  
echo without options or argument interpretation.  
Check pathnames for validity and portability.  
Loadable ksh-93 style print built-in.  
Minimal built-in clone of BSD printenv(1).  
Anyone remember TOPS-20?  
Canonicalize pathnames, resolving symlinks.  
Remove directory.  
Sleep for fractions of a second.  
Loadable built-in interface to strftime(3).  
Sync the disks by forcing pending filesystem writes to complete.  
Duplicate standard input.  
Example template for loadable built-in.  
True and false built-ins.  
Return terminal name.  
Print system information.  
Remove a directory entry.  
Print out username of current user.  
Illustrates how to build a Perl interpreter into bash.  
Convert csh aliases to bash aliases and functions. csh, xalias
Convert csh aliases to bash aliases and functions. csh, xalias
Convert csh aliases, environment variables, and variables to bash equivalents. csh, xalias
SunView TERMCAP string.  
Modified version of the Korn Shell debugger from Bill Rosenblatt's Learning the Korn Shell.
Noah Friedman's collection of scripts (updated to bash v2 syntax by Chet Ramey).  
Pseudo-arrays and substring indexing examples.  
Library functions used by require.bash.  
./scripts.noah/bash_version. bash 
A function to slice up $BASH_VERSION.  
Enable and disable eight-bit readline input.  
Make a temporary file with a unique name.  
A fun hack to translate numerals into English.  
Permissions to use the scripts in this directory.  
A way to set PS1 to some predefined strings.  
A front end to bind to redo readline bindings. readline
Lisp-like require/provide library functions for bash.  
Replacement SMTP client written in bash.  
bash replacement for cat(1). cat
Replacement for source that uses current directory.  
The string(3) functions at the shell level.  
Front-end to stty(1) that changes readline bindings too. fstty
Prompt for a yes/no/quit answer. ask
John DuBois' ksh script collection (converted to bash v2 syntax by Chet Ramey).  
Convert an arc archive to a compressed tar archive.  
Random number generator with upper and lower bounds and optional seed. random
Convert a day number to a name.  
cd replacement with a directory stack added.  
Tell what produced a core file.  
Fast man(1) replacement. manpage
Copy files using ftp(1) but with rcp-type command-line syntax.  
Change filenames to lowercase. rename lower
A nicer front end for cp(1) (has -i, etc)..  
Change the extension of a group of files. rename
A nicer front end for mv(1) (has -i, etc).. rename
Print specified pages from files.  
Permissions to use the scripts in this directory.  
A pager front end that handles compressed files.  
Poor man's top(1) for SunOS 4.x and BSD/OS.  
Rename files by changing parts of filenames that match a pattern. rename
Change the names of files that match a pattern. rename
Execute a command multiple times. repeat
Line profiler for bash scripts.  
Unarchive a (possibly compressed) tarfile into a directory.  
Carefully uudecode(1) multiple files.  
uuencode(1) multiple files.  
Print a visual display of a directory tree. tree
Show where commands that match a pattern are.  
Example scripts.  
Text adventure game in bash!  
Bourne shell's C shell emulator. csh
Readline-based pager. cat, readline pager
Center a group of lines.  
Line editor using only /bin/sh, /bin/dd, and /bin/rm.  
Recurse a tree and fix files containing various bad characters.  
The inevitable Towers of Hanoi in bash.  
Search $PATH for a file the same name as $1; return TRUE if found. inpath
Produces a random number within integer limits. random
Line input routine for GNU Bourne Again Shell plus terminal-control primitives.  
bash version of nohup command.  
Test relative precedences for && and || operators.  
Print a random card from a card deck. random
Display scrolling text.  
Display scrolling text.  
A self-reproducing script (careful!).  
Convert ls(1) symbolic permissions into octal mode.  
Display a prompt and get an answer satisfying certain criteria. ask
Display a spinning wheel to show progress.  
Give rsh(1) a shorter timeout.  
Display a tree printout of the direcotry with disk use in 1k blocks. tree
Display a graphical tree printout of dir. tree
Display a graphical tree printout of dir. tree
A web server in bash!  
Print the contents of the xterm title bar.  
Emulate printf (obsolete since printf is now a bash built-in).  
Example startup files.  
Some useful aliases (written by Fox).  
Sample startup file for bash login shells (written by Fox).  
Sample startup file for bash login shells (written by Ramey).  
Sample Bourne Again Shell init file (written by Ramey).  
Sample Bourne Again Shell init file (written by Fox).  
Example startup files for Mac OS X.  
Sample aliases for Mac OS X.  
Sample User preferences file.  
Sample Bourne Again Shell environment file.  
Sample login wrapper.  
Sample logout wrapper.  
Sample Bourne Again Shell config file.  


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.  


Groupthink : Two Party System as Polyarchy : Corruption of Regulators : Bureaucracies : Understanding Micromanagers and Control Freaks : Toxic Managers :   Harvard Mafia : Diplomatic Communication : Surviving a Bad Performance Review : Insufficient Retirement Funds as Immanent Problem of Neoliberal Regime : PseudoScience : Who Rules America : Neoliberalism  : The Iron Law of Oligarchy : Libertarian Philosophy


War and Peace : Skeptical Finance : John Kenneth Galbraith :Talleyrand : Oscar Wilde : Otto Von Bismarck : Keynes : George Carlin : Skeptics : Propaganda  : SE quotes : Language Design and Programming Quotes : Random IT-related quotesSomerset Maugham : Marcus Aurelius : Kurt Vonnegut : Eric Hoffer : Winston Churchill : Napoleon Bonaparte : Ambrose BierceBernard Shaw : Mark Twain Quotes


Vol 25, No.12 (December, 2013) Rational Fools vs. Efficient Crooks The efficient markets hypothesis : Political Skeptic Bulletin, 2013 : Unemployment Bulletin, 2010 :  Vol 23, No.10 (October, 2011) An observation about corporate security departments : Slightly Skeptical Euromaydan Chronicles, June 2014 : Greenspan legacy bulletin, 2008 : Vol 25, No.10 (October, 2013) Cryptolocker Trojan (Win32/Crilock.A) : Vol 25, No.08 (August, 2013) Cloud providers as intelligence collection hubs : Financial Humor Bulletin, 2010 : Inequality Bulletin, 2009 : Financial Humor Bulletin, 2008 : Copyleft Problems Bulletin, 2004 : Financial Humor Bulletin, 2011 : Energy Bulletin, 2010 : Malware Protection Bulletin, 2010 : Vol 26, No.1 (January, 2013) Object-Oriented Cult : Political Skeptic Bulletin, 2011 : Vol 23, No.11 (November, 2011) Softpanorama classification of sysadmin horror stories : Vol 25, No.05 (May, 2013) Corporate bullshit as a communication method  : Vol 25, No.06 (June, 2013) A Note on the Relationship of Brooks Law and Conway Law


Fifty glorious years (1950-2000): the triumph of the US computer engineering : Donald Knuth : TAoCP and its Influence of Computer Science : Richard Stallman : Linus Torvalds  : Larry Wall  : John K. Ousterhout : CTSS : Multix OS Unix History : Unix shell history : VI editor : History of pipes concept : Solaris : MS DOSProgramming Languages History : PL/1 : Simula 67 : C : History of GCC developmentScripting Languages : Perl history   : OS History : Mail : DNS : SSH : CPU Instruction Sets : SPARC systems 1987-2006 : Norton Commander : Norton Utilities : Norton Ghost : Frontpage history : Malware Defense History : GNU Screen : OSS early history

Classic books:

The Peter Principle : Parkinson Law : 1984 : The Mythical Man-MonthHow to Solve It by George Polya : The Art of Computer Programming : The Elements of Programming Style : The Unix Hater’s Handbook : The Jargon file : The True Believer : Programming Pearls : The Good Soldier Svejk : The Power Elite

Most popular humor pages:

Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : IT Slang : C++ Humor : ARE YOU A BBS ADDICT? : The Perl Purity Test : Object oriented programmers of all nations : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : The Most Comprehensive Collection of Editor-related Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor : C Humor : Scripting Humor : Real Programmers Humor : Web Humor : GPL-related Humor : OFM Humor : Politically Incorrect Humor : IDS Humor : "Linux Sucks" Humor : Russian Musical Humor : Best Russian Programmer Humor : Microsoft plans to buy Catholic Church : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor : Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor : Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor

The Last but not Least

Copyright © 1996-2016 by Dr. Nikolai Bezroukov. was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License.

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 is down you can use the at


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.

Last modified: March, 18, 2017