[toc]

Getting started

#!/bin/bash
echo "Hello World!"
[j@master bash]$ cat hello.sh 
#!/bin/bash
echo "Hello World!"
[j@master bash]$ chmod +x hello.sh 
[j@master bash]$ ./hello.sh 
Hello World!

Variables

A variable in bash can contain a number, a character, a string of characters

#!/bin/bash          
STR="Hello World!"  #No space before/after "="
echo $STR  

Local variable

#!/bin/bash
HELLO=Hello 
function hello {
         local HELLO=World
         echo $HELLO
}
echo $HELLO  #print "Hello"
hello        #invoke function hello and print "World"
echo $HELLO  #Print "Hello"

Special Shell Variables

Variable Meaning
$0 Filename of script
$1 Positional parameter #1
$2 - $9 Positional parameters #2 - #9
${10} Positional parameter #10
$# Number of positional parameters
"$*" All the positional parameters (as a single word) *
"$@" All the positional parameters (as separate strings)
${#*} Number of positional parameters
${#@} Number of positional parameters
$? Return value
$$ Process ID (PID) of script
$- Flags passed to script (using set)
$_ Last argument of previous command
$! Process ID (PID) of last job run in background

Conditional expressions and if statements

if

if test-commands; then
  consequent-commands;
[elif more-test-commands; then
  more-consequents;]
[else 
alternate-consequents;]
fi
#!/bin/bash
if [ "$1" == "yes" ];then
echo yes
elif [ "$1" == "no" ];then
echo no
else
echo "Please input yes or no"
fi

case

#!/bin/bash

show_help(){
echo "Usage: ${0##*/} [-h|--help] [-v|--verbose] [-l|--lines number] FILE... "
}

while (( "$#" )); do
    case $1 in
        -h|--help)
            show_help
            exit
            ;;
        -l|--lines) 
            if [ "$2" ] && [ ${2:0:1} != "-"  ]; then
                number=$2
                shift
            else
                echo "Option -l|--lines needs an argument" 
                show_help
                exit 1
            fi
            ;;
        -v|--verbose)
            verbose=1 
            ;;
        -?*)
            echo "Option $1 is not supported."
            show_help
            exit 1
            ;;
        *)       
            break
    esac
    shift
done

select

The select construct, adopted from the Korn Shell, is yet another tool for building menus

select variable [in list]
do
 command...
 break
done

((..))

(( expression ))
(( 5 > 4 ))                                      # true
echo "Exit status of \"(( 5 > 4 ))\" is $?."     # 0

(( 5 > 9 ))                                      # false
echo "Exit status of \"(( 5 > 9 ))\" is $?."     # 1

i=1
(( i++ )) # i=2

a=$(( 3+6 ))  #a=9
let a++       #a=10 now

[[…]]

[[ expression ]]

Return a status of 0 or 1 depending on the evaluation of the conditional expression expression

#!/bin/bash
file=/etc/passwd

if [[ -e $file ]]; then
  echo "Password file exists."
fi

Expression

( expression ) #Returns the value of expression
! expression   #True if expression is false
expression1 && expression2 #True if both expression1 and expression2 are true
expression1 || expression2 #True if either expression1 or expression2 is true

Test Operators

Arithmetic Comparison

Operator meaning example
-eq Equal to if [ “$a” -eq “$b” ]
    if [[ $a -eq $b ]]
-ne Not equal to if [ “$a” -ne “$b” ]
-lt Less than if [ “$a” -lt “$b” ]
-le Less than or equal to if [ “$a” -le “$b” ]
-gt Greater than if [ “$a” -gt “$b” ]
-ge Greater than or equal to if [ “$a” -ge “$b” ]

Arithmetic Tests using (( ))

> Greater than
>= Greater than or equal to
< Less than
<= Less than or equal to

Example:

#!/bin/bash
var1=5
var2=2
if (( $var1 > $var2 )); then
  echo "$var1 is greater than $var2"
fi

string comparison

Operator Meaning Example
= Equal to if [ “$a” = “$b” ]
== Equal to if [ “$a” == “$b” ]
!= Not equal to if [ “$a” != “$b” ]
\< Less than if [[ “$a” < “$b” ]]
    if [ “$a” \< “$b” ]
\> Greater than if [[ “$a” > “$b” ]]
    if [ “$a” > “$b” ]
-z String is empty if [ -z “$String” ]
-n String is not empty if [ -n “$String” ]

File test

if [[ -e "file.txt" ]]; then
  :  # do nothing
fi
Operator Tests Whether —– Operator Tests Whether
-e File exists   -s File is not zero size
-f File is a regular file      
-d File is a directory   -r File has read permission
-h File is a symbolic link   -w File has write permission
-L File is a symbolic link   -x File has execute permission
-b File is a block device      
-c File is a character device   -g sgid flag set
-p File is a pipe   -u suid flag set
-S File is a socket   -k “sticky bit” set
-t File is associated with a terminal      
         
-N File modified since it was last read   F1 -nt F2 File F1 is newer than F2 *
-O You own the file   F1 -ot F2 File F1 is older than F2 *
-G Group id of file same as yours   F1 -ef F2 Files F1 and F2 are hard links to the same file *
         
! NOT (inverts sense of above tests)      

Loop for , while and until

for

#!/bin/bash
for i in $( ls ); do
    echo item: $i
done
#!/bin/bash
for i in `seq 1 10`;
do
    echo $i
done

while

The while executes a piece of code if the control expression is true, and only stops when it is false (or a explicit break is found within the executed code

#!/bin/bash 
COUNTER=0
while [  $COUNTER -lt 10 ]; do
     echo The counter is $COUNTER
     let COUNTER=COUNTER+1 
done

until

The until loop is almost equal to the while loop, except that the code is executed while the control expression evaluates to false

#!/bin/bash 
COUNTER=20
until [  $COUNTER -lt 10 ]; do
     echo COUNTER $COUNTER
     let COUNTER-=1
done

Functions

Functions with parameters sample

#!/bin/bash 
function quit {
    exit
}  
function e {
    echo $1 
}  
e Hello
e World
quit
echo foo 

Return value

#!/bin/bash
myfunc() {
    local myresult='some value'
    echo $myresult
}
result="$(myfunc)"

Redirection

Files Meaning File descriptor
stdin the keyboard 0
stdout the screen 1
stderr error messages output to the screen 2
2>filename # Redirect stderr to file "filename."
2>>filename # Redirect and append stderr to file "filename."
2>&1  # Redirects stderr to stdout.
&>filename # Redirect both stdout and stderr to file "filename."
&>/dev/null #Redirect both stdout and stderr to null
#!/bin/bash
#read lines into array
myarr=()
while read line; do
    myarr+=("$line")
done <file.txt

Here Documents

[j@master bash]$ cat <<End-of-message
> -------------------------------------
> This is line 1 of the message.
> This is line 2 of the message.
> This is line 3 of the message.
> This is line 4 of the message.
> This is the last line of the message.
> -------------------------------------
> End-of-message
-------------------------------------
This is line 1 of the message.
This is line 2 of the message.
This is line 3 of the message.
This is line 4 of the message.
This is the last line of the message.
-------------------------------------
[j@master bash]$

Here strings

[j@master bash]$ read first second <<< "hello world"
[j@master bash]$ echo $second $first
world hello

Array and Dictionary

myarr=()             #Make an empty array
myarr=(a b c 3 2 1)  #Initialize an array
${myarr[4]}          #Getting 5th element
${myarr[@]}          #Getting all emements
${!myarr[@]}         #Getting indices
${#myarr[@]}         #Getting length
myarr[2]=3           #Modify 3nd element
myarr+=(x y z)       #Append elements
myarr=( $(ls) )      #Save `ls` out as an array
${myarr[@]:s:n}      #Retrieve n elements beginning at index s
unset myarr[0]       #Destroy 1st element
unset myarr          #Remove the entire array

See more Bash array

Regular Expressions

     
. dot matches any one character, except a newline
? question mark matches zero or one of the previous RE
* asterisk matches zero or N numbers of repeats of the preceding character
+ The plus matches one or more of the previous RE
^ caret matches the beginning of a line
$ dollar sign at the end of an RE matches the end of a line
[…] Brackets enclose a set of characters to match in a single RE
[xyz]   matches x OR y OR z
[a-z0-9]   matches any single lowercase letter or any digit
\$   matches “$”
\<the\>   matches “the” but not “them” ,”other”
[0-9]{5}   matches file digits
( ) Parentheses enclose a group of REs
| or matches any of a set of alternate characters

postfix

   
[:alnum:] matches alphabetic or numeric characters. This is equivalent to **A-Za-z0-9**
[:alpha:] This is equivalent to **A-Za-z**
[:blank:] matches a space or a tab
[:cntrl:] matches control characters
[:digit:] This is equivalent to **0-9**
[:graph:] Matches characters in the range of ASCII 33 - 126. This is the same as **[:print:]**, below, but excluding the space character
[:lower:] matches lowercase alphabetic characters. This is equivalent to **a-z**
[:print:] Matches characters in the range of ASCII 32 - 126. This is the same as **[:graph:]**, above, but adding the space character.
[:space:] matches whitespace characters (space and horizontal tab)
[:upper:] matches uppercase alphabetic characters. This is equivalent to **A-Z**
[:xdigit:] matches hexadecimal digits. This is equivalent to **0-9A-Fa-f**
#!/bin/bash
# 3-5 lower letters + 5 digits + @ + something.com
REGEX='^([a-z]{3,5}[0-9]{5})\@(.+\.com)$'
if [[ $1 =~ $REGEX ]]; then
echo "Username is ${BASH_REMATCH[1]}"
echo "Domain is ${BASH_REMATCH[2]}"
else
echo "Not a supported mail address "
fi
[j@master bash]$ ./1.sh user12345@gmail.com
Username is user12345
Domain is gmail.com
[j@master bash]$ 
[j@master bash]$ ./1.sh user123456@gmail.com
Not a supported mail address

Shell expansions

Brace Expansion

bash$ echo a{d,c,b}e
ade ace abe

Parameter Substitution and Expansion

Expression Meaning
${var} Value of *var* (same as *$var*)
   
${var-$DEFAULT} If *var* not set, evaluate expression as *$DEFAULT* *
${var:-$DEFAULT} If *var* not set or is empty, evaluate expression as *$DEFAULT* *
   
${var=$DEFAULT} If *var* not set, evaluate expression as *$DEFAULT* *
${var:=$DEFAULT} If *var* not set or is empty, evaluate expression as *$DEFAULT* *
   
${var+$OTHER} If *var* set, evaluate expression as *$OTHER*, otherwise as null string
${var:+$OTHER} If *var* set, evaluate expression as *$OTHER*, otherwise as null string
   
${var?$ERR_MSG} If *var* not set, print *$ERR_MSG* and abort script with an exit status of 1.*
${var:?$ERR_MSG} If *var* not set, print *$ERR_MSG* and abort script with an exit status of 1.*
   
${!varprefix*} Matches all previously declared variables beginning with *varprefix*
${!varprefix@} Matches all previously declared variables beginning with *varprefix*

**** If *var* *is set, evaluate the expression as *$var* with no side-effects.

#01  If parameter not set, use default
var1=1
var2=2
# var3 is unset.

echo ${var1-$var2}   # 1
echo ${var3-$var2}   # 2

#02  If parameter not set, set it to default
echo ${var=abc}   # abc
echo ${var=xyz}   # abc
# $var had already been set to abc, so it did not change.

#3  If parameter set, use alt_value, else use null string
a=${param1+xyz}    # param1 is not set
echo "a = $a"      # so a = 

param2=            # param2 is set
a=${param2+xyz}
echo "a = $a"      #so a = xyz

#4  If parameter set, use it, else print err_msg and abort the script with an exit status of 1
#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT

String Operations

Expression Meaning
${#string} Length of *$string*
   
${string:position} Extract substring from *$string* at *$position*
${string:position:length} Extract *$length* characters substring from *$string* at *$position* [zero-indexed, first character is at position 0]
   
${string#substring} Strip shortest match of *$substring* from front of *$string*
${string##substring} Strip longest match of *$substring* from front of *$string*
${string%substring} Strip shortest match of *$substring* from back of *$string*
${string%%substring} Strip longest match of *$substring* from back of *$string*
   
${string/substring/replacement} Replace first match of *$substring* with *$replacement*
${string//substring/replacement} Replace all matches of *$substring* with *$replacement*
${string/#substring/replacement} If *$substring* matches front end of *$string*, substitute *$replacement* for *$substring*
${string/%substring/replacement} If *$substring* matches back end of *$string*, substitute *$replacement* for *$substring*

***** Where *$substring* is a Regular Expression

#1   ${#string}
a="abc"
echo ${#a}  # get 3

#2 ${string:position}
a="abc123"
echo ${a:3}  #get 123

#3 ${string#substring}
a="000123"
echo ${a#0}  # get 00123
echo ${a##+(0)}  # get 123

#4 ${string%substring}
a="123000"
echo ${a%0}  # get 12300
echo ${a%%+(0)}  #get 123

#5 ${var/Pattern/Replacement}
a="a123a"
echo ${a/a/A} #get A123a
echo ${a//a/A} #get A123A

#6 ${var/#Pattern/Replacement}  ${var/%Pattern/Replacement}
[j@master bash]$ a="X000X"
[j@master bash]$ echo ${a/#X/Y}
Y000X
[j@master bash]$ echo ${a/%X/Y}
X000Y

MISC

read user input

#!/bin/bash
echo Please, enter your firstname and lastname
read FN LN 
echo "Hi! $LN, $FN !"

Trap error

#! /bin/bash

err_report() {
    echo "Error on line $1"
}

trap 'err_report $LINENO' ERR

echo hello | grep foo  # This is line number 9
 $ ./test.sh
 Error on line 9

Trap on EXIT

#!/bin/bash

trap 'echo Variable  a = $a ' EXIT

a=11
a=22
$ ./test.sh 
Variable a = 22

Logging

#!/bin/bash
exec >> logfile.log 2>&1
date
set -x

Reference