Skip to content

bash

A good cheatsheet : https://devhints.io/bash

This page was made for a job application and is not finished yet.

startup files

.profile

This is meant to do a one time setup when logging in, and .bashrc is run at every subsequent bash only startup.

bash_profile should just load .profile and .bashrc in that order.

command line options

  • -x : print all commands as encountered

Also to be set in the script like so :

verbose output
1
2
3
4
5
#!/bin/bash 

echo "Hello"
set -x
echo "World"

The -x kicks in when encountered so this would print :

output
1
2
3
Hello
+ echo World
World

Started like this would print :

cli -x option
bash -x test.sh
output
1
2
3
4
5
+ echo Hello
Hello
+ set -x
+ echo World
World

Try setting -x in your shell :)

console and reset with +x
1
2
3
set -x
# Aiii
set +x # pfoe, this reverts -x 

variables

Important

No spaces between = and operands!

Important

All variables are strings !!!

Yes, they get converted to number when used in arithmetic.

numeric
VAR=11
interp=interpolation
varia="hello with spaces and $interp"
vari3='hello with spaces and no $interp'

echo $varia
echo $vari3

echo VAR
echo $VAR
echo ${VAR}
echo $(VAR) # error !

The result is :

interpolation
1
2
3
4
5
6
hello with spaces and interpolation
hello with spaces and no $interp
VAR
11
11
./test.sh: line 13: VAR: command not found

So beware of interpolation, and single/double quotes.

comments

comments
1
2
3
4
5
6
# isse siemple !!

var = also
int = $var  # with interpolation !
var1 = "a single word can come without quotes"
var1 = "but these cannot"

input

read gets one whole line upto newline :

user input
1
2
3
4
echo -n "give a line : " # -n stops newline
read var 

echo $var

command input

Getting the output of a command into a string can be done with back quotes

backticks
1
2
3
lst=`ls`  # NO spaces 

echo $lst

Note that wildcards are special and get expanded :

wildcards
1
2
3
MESSAGE="I am a rock *"
echo $MESSAGE
echo "$MESSAGE"

Prints this: because i have these files in my directory (1 2 3 4 and test.sh)

output
I am a rock 1 2 3 4 test.sh
I am a rock *

So enclosing the $MESSAGE in "" fixes the expansion so you can see here it happens in the echo command not the assignment !! This is done because you also want that to happen when you say :

also
ls *

stopping

exit value
1
2
3
4
exit 100 # that simple
# the exit value can be printed with 
echo $?
100

arithmetic

As said variables are always strings, when you want to do arithmetic with them you have to use let

let
1
2
3
4
5
6
a=11
b=21
x=$a$b
echo $x  # prints 1121
let x=a+b
echo $x  # prints 32

Since everything remain strings anyway, you can just as well do :

let
let x="8*5"
echo $x # 40 (or actually "40" internal)

Also x itself can participate :

arithmetic
1
2
3
4
x=32
let x="5 * 8 + ++x"

echo $x # prints 73 # (5*8 + (++32))

Note that the quotes are needed :

arithmetic
1
2
3
4
5
x=32
let x=5 * 8 + ++x
# let: test.sh: syntax error....

show what happens with echo or bash -x
arithmetic
1
2
3
+ x=32
+ let x=5 1 2 3 4 test.sh 8+ ++x
./test.sh: line 5: let: test.sh: syntax error: invalid arithmetic operator (error token is ".sh")

Again the * got expanded to the files in the current directory.

You can omit the quotes if you don't use spaces, but its not very readable :

spaces
let x=5*8+++x

expr and $(expr)

Another way do evaluate expression without the need for a variable because it defaults to printing the outcome. Also expr takes separate arguments, so now the spaces are actually needed.

expr
1
2
3
4
5
6
expr 1 + 1   # prints 2 to stdout
RESULT=$(expr 3 * 3)
echo $RESULT

# note the spaces are needed now :
expr 1+1     # prints 1+1

But now you have to make effort to get it into a variable, with the ( ) and still you need to escape the wildcard !

double parentheses

This seems the best usable version :

double (( ))
1
2
3
4
5
6
#!/bin/bash

# within the (( )) spaces allowed and no escapes needed
x=$(( 1+1 * 3 ))

echo $x # prints 4 so operation precedence works

In this format you can also use /= *= \%=, ..

double parentheses
1
2
3
x=2
(( x *= 10))
echo $x # prints 20

Important

In expression evaluation (( )) is clearly the winner.

if statements

Important

0 == true and 1 == false. YES 0 means all ok and nonzero means error in bash!!

The general format is :

if logic
if expression; then command; else command; fi

That's the one liner version and it needs terminators ; to work. Multi line and more readable and with elif variant :

if logic
1
2
3
4
5
6
7
8
9
if conditional expression
then
    command
elif conditional expression
then
    command
else
    command
fi

Important

when nesting be careful to close blocks with fi

To test the conditional you always use the test function, which you can just use on the commandline :

testing
test 5 -gt 9
echo $? 

Important

Again this prints 1, because it is NOT ok(0)

However in if statements this works without showing the numbers and there is also a shortcut for the test command :

testing
if test 5 -gt 9; then
    echo "Smaller"
else
    echo "Bigger"
fi

# this can be written as ( [ ] is actually a command in bash) :
if [ 5 -gt 9 ]; then
    echo "Smaller"
else
    echo "Bigger"
fi

Important

Mind the spaces in the [] command, a space around all operands/operators !

If you need to compare variables, include them within DOUBLE quotes to make them single arguments to test, for instance

compare between ""
1
2
3
4
5
6
7
8
9
x="i am groot"
y="i am groot"

if [ $x != $y ]; then echo "not same"; fi
# too many arguments : 
if [ "$x" != "$y" ]; then echo "not same"; fi
# 
if [ '$x' != '$y' ]; then echo "not same"; fi
# not same

To explain what happens, use -x again :

-x version
1
2
3
4
5
6
# without quotes, too many args
+ '[' i am groot '!=' i am groot ']' 
# with double quotes the are equal :
+ '[' 'i am groot' '!=' 'i am groot' ']'
@ with single quotes : not same ;)
+ '[' '$x' '!=' '$y' ']'

Without interpolation, they are surely not the same.

comparison operators

For strings we use the standard ones, and for numbers the -gt ones.

Operator Purpose For DataType
= Equal to operation string
== != Equal to operation is not equal to string string
< i s less than in ASCII alphabetical string
> i s greater than in ASCII alphabetical string
-z if a string is empty (or null) string
-n if a string is not empty (or not null) string
-eq is equal to number
-ne is not equal to number
-lt is less than number
-le is less than or equal to number
-gt is greater than number
-ge is greater than or equal to number

Random variables

You can generate random number through a variable, the numbers are between 0 and 32767 :

random
echo $RANDOM $RANDOM $RANDOM $RANDOM
echo $(($RANDOM % 11)) # 0-10 inclusive

sequence list

generating sequences on the fly :

sequences
1
2
3
4
5
6
echo {0..10}            # 0 1 2 3 4 5 6 7 8 9 10
for x in {0..10}; do    # use them in a loop
    echo $x
done
# sequence can be used for step size
echo $(seq 10 -2 4)     # 10 8 6 4

You cannot seem to assign sequences to a list or something.

for loop

syntax :

for logic
1
2
3
4
for iterator
do
    # action
done

With the 'in' iterator we can walk through lists

for in list
# prints what you expect
for WORD in "Hello" "World" "*" "Nice" "to" "meet" "you."; do
    echo "The world is: $WORD"
done

# file wildcard
for WORD in *; do
    echo "The world is: $WORD"
done
# in my directory it prints  : 
The world is: 1
The world is: 2
The world is: 3
The world is: 4
The world is: hubba
The world is: test.sh

So .. changing the '*' in the first example would just mix the sentence with the directory content.. Even this does what you think it does :

mixing
1
2
3
for WORD in "Hello" "World" * "Nice" {0..6} "to" "meet" "you."; do
    echo "The world is: $WORD"
done

More useful is traversing a variables content:

traverse words
1
2
3
4
FRUITS="Mango Apple Banana"
for FRUIT in "$FRUITS"; do
    echo "The fruit is: $FRUIT"
done

Important

If you drop the 'in x' part bash will take the commandline variables

c style loop

c style for
1
2
3
4
for (( i=0; i<5; i++ )) # won't work with single ( )
do
    echo $i
done

while do

wildcards

Sort of regular expressions, but with a different syntax. For instance . is not 'any' character but stands for directory on it's own, asterisk is the same as .* in PCRE. And more, here are some examples.

ls
1
2
3
4
5
6
7
ls .
ls ..
ls *.jpg
ls ../*.*
ls *.??? # list all files with tree digit extension
ls [a-d]* # all files beginning with a,b,c or d
ls [!ab]* # qll files not beginning with a or b

You can enable extended globbing to have more features. This command shows if extglob is enabled. It seems to be by default:

extglob
1
2
3
4
5
6
shopt extglob
# extglob         on
# if not :
shopt -s extglob
# or disable with
shopt -u extglob

You can now do more then before, an example :

extglob
# won't work with shopt -u extglob
ls !(*.txt|*.db) 
pattern description example matches
!(patterns) Match anything that does not match the 'patterns' !(photo).db photophoto.db photos.db photoss.db photosss.db
?(patterns) Match zero or one occurrences of the 'patterns' photo?(photo).db photo.db photophoto.db
*(patterns) Match zero or more occurrences of the 'patterns' photo*(s).t* photo.txt photos.txt photoss.txt photosss.txt
+(patterns) Match one or more occurrences of the 'patterns' photo+(s).txt photos.txt photoss.txt photosss.txt
@(patterns) Match one occurrence of the 'patterns' photo@(s).db photos.db

You can also use these patterns in [[]] expressions :

double brackets
1
2
3
4
5
6
7
RESPONSE="Error: something went wrong."
# Match 1 or more occurances of Error follow by any amount of chars
if [[ $RESPONSE == +(Error)* ]]; then
    echo "Performing: failure handling"
else
    echo "Performing: success handling"
fi

Note that single brackets will lead to an error here :

single brackets
1
2
3
4
5
6
# if [ $RESPONSE == +(Error)* ]; then
./t.sh: line 5: [: too many arguments

# and this gives no error, but doesn't match :
if [ $RESPONSE == "+(Error)*" ]; then
"Performing: success handling

Of course the last one just tries to match "+(Error)*" literally.

switch case

switch
x="hallo"

case x in
    hello)
        echo "Hello";
    ;;

    h\*o)
        echo "Hallo glob";
    ;;&     # ;& would also work

    hallo)
        echo "Also Hallo"
    ;;

    \*)
        echo "Never reached"
    ;;

esac

Warning

case/esac does NOT fallthrough like in C. In newer bash versions you can use ;& or ;;& for that.

positional arguments

$# prints the number of arguments, not counting program. \(1-\)9 print positional parameters in functions and scripts. $* prints all arguments, excluding $0

arguments
1
2
3
4
5
6
7
8
#!/bin/bash
echo $#
echo $0
echo $1
echo $2
echo $*

exit 266; # returns 10 !! only 1 char available

Calling this script with these arguments :

arguments
1
2
3
4
5
6
./t.sh 1 2 3 4
4
./t.sh
1
2
1 2 3 4

arrays

!/bin/bash

ARRAY=(Apple "Orange" Mango)

echo ${ARRAY[0]} # $ARRAY[0] is not valid echo "At index 1: ${ARRAY[1]}" # string interpolation ARRAY[2]=Banana # update value at index 2 ARRAY[3]=Papaya # add new value at index 3 echo ${ARRAY[2]} ${ARRAY[3]}

Note that $ARRAY[0] prints "Apple[0]" so $ARRAY evaluates to the first member. Also you can use the * wildcard to represent 'all members' so :

arrays
1
2
3
4
5
6
7
echo ${#ARRAY[*]}     # 4
echo ${ARRAY[*]}      # Apple Orange Banana Papaya

# traversing :
for x in ${ARRAY[*]}; do
    echo $x
done