Bash scripting cheat sheet

Some bash scripting notes I keep as a reference.

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##*.}"

loop through files (including hidden) in folder

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.

Leave a Reply