Basics
- case sensitive.
- comment:
#
. - space sensitive: be careful where to and where not to put spaces.
- use $var<sub>name</sub> to retrieve variable value.
Variables
- No data types in bash variable. Can store a number, a character, a string.
- No need to declare a variable.
- Case sensitive.
E.g.
#!/bin/bash
str="Hello world"
echo $str
NOTE: no spaces between = when assigning variables.
Special variables
$0
: name of the bash script.$1
–$9
: command line arguments.$#
: number of arguments passed to script.$@
: all arguments passed to script.$?
: exit status of the most recent process.$$
: process ID of the current script.$USER
: username of the user running the script.$HOSTNAME
: hostname of the system running the script.$SECONDS
: number of seconds since start of script.$RANDOM
: get a random number.$LIENO
: current line number in the script.
Get variable length
${#var}
E.g.
a='hello world'
echo ${#a} # 11
b=4999
echo ${#b} # 4
If conditions
NOTE the space inside [[]].
if [[ expression ]]; then
...
fi
if [[ expression ]]; then
...
elif [[ expression ]]; then
...
fi
Logical operators
expression | meaning |
---|---|
! expression | NOT |
-n str | length of str > 0 |
-z str | length of str = 0 |
str1 = str2 | str1 is the same as str2 |
str1 != str2 | str1 is not the same as str2 |
int1 -eq int2 | integer equality |
int1 -gt int2 | int1 > int2 |
int1 -lt int2 | int1 < int2 |
int1 -ge int2 | int1 >= int2 |
int1 -le int2 | int1 <= int2 |
-d file | file exists and is a directory |
-e file | file exists |
-r file | file exists and read permission is on |
-w file | file exists and write permission is on |
-x file | file exists and exe permission is on |
-s file | file exists and size > 0 |
&& | logical and |
|| | logical or |
NOTE:
=
is equivalent to==
.=
is not the same as-eq
:=
is for string comparison.eq
is for int comparison.
E.g.
test 1 = 1
test 01 = 1
test 01 -eq 1
E.g.
[[ -r $1 ]] && [[ -s $1 ]]
[[ $USER = 'bob' ]] || [[ $USER = 'andy' ]]
NOTE: space needed around operators. E.g.
[[ $foo==0 ]] # THIS WRONG
Quotes
When setting a string variable:
- when a string is a single word, no need to use quotes.
- when string has > 1 word, need to use quotes.
- single quotes: treat everything literally.
- double quotes: allow substitution.
- can use double quotes to preserve multi-line output. See 8.
E.g.
var1=hello # dont need quotes for single word
var2="hello world" # nedd quotes for multi word
var3='hello $var1' # output `hello $var1`
var4="hello $var1" # output `hello hello`
NOTE that the tilde symbol ~
is NOT expanded inside double quotes. The following is WRONG:
Instead, use $HOME
.
Command substitution
Save the output of a command into a variable. Syntax:
$(command)
or
$( command )
NOTE: if the output of the command has > 1 lines, newlines will be removed and the output ends up as a single line. To preserve multi-line, add double quotes:
"$( command )"
E.g.
filename=/var/backup-$(date +%Y%m%d)
User input
To take input from user, user read
.
Syntax:
read var1 # read input from user and save it to a variable var1
Options:
-p
: add a prompt.-s
: input hidden.
E.g.
read -p 'username: ' uservar
read -sp 'password: ' pass
Read from STDIN
Bash accommodates piping and redirecting by way of special files. Each process gets its own set of files and they are linked when piping or redirection is invoked:
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
| STDIN | /dev/stdin | /proc/self/fd/0 | | STDOUT | /dev/stdout | /proc/self/fd/1 | | STDERR | /dev/stderr | /proc/self/fd/2 |
To make a bash script able to process data that is piped to it, need to read the relevant file. E.g.
In script.sh
:
#1/bin/bash
cat /dev/stdin | sort
In shell:
ls ~ | ~/script.sh
Arithmetic
There are several ways to do simple math in bash.
double parentheses (recommended): $((expression))
E.g.
a=$((4+5))
b=$(( 4+5 ))
c=$(( a+5 )) # NOTE: use variable without leading $
d=$(( $b+5 )) # NOTE: use variable with leading $
((b++)) # increment
a=$((4*5)) # no need to escape *
let
syntax:
let expression
: NO spaces.let "expression"
: with spaces.
E.g.
let a=5+4 # no space
let "a = 5 + 4" # with spaces
let a++
let a=10*2
let b=a-1
expr
similar to let
, but instead of saving to a variable, prints out the result.
NOTE: when using expr
:
-
with spaces: evaluate expression. e.g.
expr 5 + 4
-
without spaces or with quotes: print out expression. e.g.
expr 5+4 # 5+4 expr "5 + 4" # 5+4
while loop
syntax:
while [[ condition ]]; do
...
done
or
while [[ condition ]]
do
...
done
E.g.
n=1
while [[ $n -lt 10 ]]; do
echo "n=$n"
((n++))
done
NOTE: break
and continue
have their conventional uses.
until loop
syntax:
until [[ condition ]]; do
...
done
for loop
syntax:
for var in <list>; do
...
done
where <list>
is a series of strings, separated by spaces.
or
for (( i=0; i<10; i++ )); do
...
done
E.g.
for ii in 'x y z'; do
echo $ii
done
Number ranges
syntax:
{x..y} # ints from x to y
{x..y..s} # from x to y with step s
NOTE: integer only NOTE: can be x > y or x < y
E.g.
for i in {1..5}; do
echo $i
done
{10..0..2} # 10,8,6,4,2,0
case statement
syntax:
case <variable> in
<pattern1>)
...
;;
<pattern2>)
...;;
...
esac
e.g.
case $1 in
10)
echo var is 10;;
20)
echo var is 20;;
*)
echo something else;;
esac
select statement
Can be used to create a simple menu. syntax:
select var in <list>; do
...
done
Items in <list>
will be shown on screen, with a number assigned to each. A prompt is printed allowing the user to select a number. The user types the number and enter, the corresponding value is given to <var>
, then executes the commands the commands between do
and done
. Once finished a prompt will be shown again.
NOTE:
- if user types an invalid number then <var> becomes null.
- if user hits enter without selecting any of the listed options, the options are displayed again.
- can use break to stop the loop.
- can change the variable
PS3
to change the default prompt.
E.g.
names='andy bob pewd quit'
PS3='select character:'
select name in $names; do
if [[ $name = 'quit' ]]; then
break
fi
echo Hello $name
done
functions
function definition
syntax:
func_name () {
...
}
or
function func_name () {
...
}
NOTE:
- the
()
is always empty. - function definition needs to appear before function call.
- No input args in definition. Input args are put after function name and are accessed through
$1
,$2
, etc..
E.g.
function func () {
echo $1
}
func Mars
func JJ
return values
Can use return
to return a value.
Bash function can only return integers, indented to indicate the status (0 for success, > 0 for errors).
Can use $? to retrieve the returned status.
E.g.
function print() {
echo Hello $1
return 5
}
print Mars
echo function return value is $?
To make functions return other values, can use Command Substitution as a workaround: let the function print the desired result (and the result only), then put the function call in a "$()"
.
E.g.
func () {
cat $1 | wc -l
}
lines="$( func $1 )"
echo number of lines is "$lines".
variable scopes
Default to global.
To create a local variable in functions, use the local
keyword.
E.g.
functon hello () {
local var1='local'
echo $var1
}
overriding commands
A function can be named as an existing bash command, this allows wrapper functions.
E.g.
function ls () {
command ls -lh
}
NOTE: need to put command
keyword in front of the command to be overridden, otherwise it will recurse into endless loop.
file path, dirname, basename and extension
To split folder path and filename of the absolute path "filepath":
dirname="$(dirname "$filepath")"
basename="$(basenaem "$filepath")"
To split file name and extension from "basename":
filename="${basename%.*}"
ext="${filename##*.}"
for file in /path/to/folder; do
...
done
This will not include hidden files in the list.
To include hidden files but not .
and ..
:
for file in /path/to/folder/* .[^.]*; do
...
done
syntax checking
shellcheck
shellcheck: is a shell script static analysis tool. The online version: https://www.shellcheck.net/
To check a script:
shellcheck script.sh
Export variables
Bash variables are limited to the process they are created in.
If a script runs another script as one of its commands, we need to export the variables to make them available to the 2nd script.
Syntax:
export variable_name
What happens when exporting a variable:
We tell bash that everytime a new process is created, it makes a copy of the variable and hands it over to the new process.
Exported variables have the same names in different processes, but are not related to each other.
Export a variable is a one-way process: changes on the exported variables in new processes don’t effect the original ones.
E.g.
In `script1.sh`:
#!/bin/bash
var1=blah
var2=foo
echo $0: var1=$var1, var2=$var2
export var1
./script2.sh
echo $0: var1=$var1, var2=$var2
In `script2.sh`:
#!/bin/bash
echo $0: var1=$var1, var2=$var2
var1=flop
var2=blah
Run script1.sh, the output is:
script1.sh: var1=blah, var2=foo script2.sh: var1=blah, var2= script1.sh: var1=blah, var2=foo
Optional command line argument
Normally, $1
, $2
etc. will get the 1st, 2nd command line arguments.
If an command line argument is optional, refer to it using, e.g.
${2:-5}
.
If the 2nd argument is not given, it will have a default value of 5.