0% found this document useful (0 votes)
32 views105 pages

Shellman 2021 02 19

This document is the contents page for a book titled "Shellman Bash Scripting" by Remisa Yousefvand. It provides an overview of the chapters and topics that will be covered in the book, including shell scripting basics, variables, functions, commands, loops, logic, strings, arrays, files and more. The book aims to teach readers how to write shell scripts and scripts for automating tasks and programming in the shell. It uses Leanpub for publishing and provides the publish date of the book as 2021-02-19.

Uploaded by

icache2010
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views105 pages

Shellman 2021 02 19

This document is the contents page for a book titled "Shellman Bash Scripting" by Remisa Yousefvand. It provides an overview of the chapters and topics that will be covered in the book, including shell scripting basics, variables, functions, commands, loops, logic, strings, arrays, files and more. The book aims to teach readers how to write shell scripts and scripts for automating tasks and programming in the shell. It uses Leanpub for publishing and provides the publish date of the book as 2021-02-19.

Uploaded by

icache2010
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 105

Shellman Bash Scripting

Remisa Yousefvand
This book is for sale at http://leanpub.com/shellman

This version was published on 2021-02-19

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

© 2018 - 2021 Remisa Yousefvand


To my mom and daddy. Thank you for your unconditional support and for being the source of
passion and inspiration in my life.
Contents

Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i

Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii

Shellman Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii

Shell Scripting Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1


Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
shebang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Run a Shell Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Run a Command from Shell Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Multiline Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Variable Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Argument parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Organizing your Shell Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Event handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Double Quote vs Single Quote vs Backtick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Sample scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
cmd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
CONTENTS

file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
archive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
http . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
ftp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
ip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
crypto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
lib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Argument Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Nested Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Colorful Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Preface
Shellman¹ is a vscode² snippet extension and is made to provide a high level interface for writing
shell scripts. That means easy way to accomplish the job without worrying about the details. As
long as you understand the interface and how it is organized, you are good. The trade-off is you
miss the details and it is also a good thing. After all that’s the purpose of abstraction. When you
need more control you can dig dipper and deal with details but before that stay at high ground as
much as you can.
When I started shell scripting, even with the best tools available I found it unorganized. I couldn’t
find easily how to do file, string, array… related operations. I didn’t care how a string is reversed
in shell scripting as long as it works (I know about best practices, performance, compatibility… but
they are not my primary concerns in a new field). Give some code the string abc which transforms
it to cba. If you are coming from a OOP background you expect³ such a function in String class.
So Shellman organizes such operations under related abstract groups called namespaces and I just
searched internet to find most fitting codes that do the job and organized them.
The hard part of shell scripting is not shell scripting itself, it is understanding Linux and knowing
the correct command and switches, so if you can do it in terminal, you can do it easily via shell script
too. Shell scripting is useful for common tasks automation.
This book is a guide for beginners who want to start shell scripting with Shellman effectively. If you
are of pragmatic type people then go ahead and read Basics section and desired namespaces. Also
the business model of Shellman is published on medium⁴.

Remisa Yousefvand
July 2020
¹https://marketplace.visualstudio.com/items?itemName=Remisa.shellman
²https://code.visualstudio.com
³From cognitive/statistical point of view, coming from OOP, or at least expecting order, you find Shellman convenient because its structure
matches your beliefs (prior). The probability distribution curve has the same shape, so you learn fast (little update to your curve is needed).
Your wishes about where to find a function just comes true.
⁴https://medium.com/@remisa.yousefvand/shellman-reborn-f2cc948ce3fc
Prerequisites
• vscode⁵ IDE

vscode download

• Shellman⁶ snippet

shellman install
⁵https://code.visualstudio.com
⁶https://marketplace.visualstudio.com/items?itemName=Remisa.shellman
Shellman Structure
Shellman divides its content into semantical categories named namespace. The concept is already
familiar to programmers, but in simple words it means keeping related materials together under a
generic name. So if you need to do something with String like changing it to upper case then it
makes sense to look at string namespace.

String Namespace

When you press ENTER on an item like above picture, vscode inserts some code into your script which
you can move into different parts using TAB key. This is called snippet. To access a snippet you start
typing and vscode shows a menu of snippets with matching prefixes.

Snippet Alias
When a snippet can be activated by two or more prefixes, a | between prefixes is used to
indicate that.

Shellman is structured into namespaces, so it is useful to know supported namespaces and their
members. There is no order in learning namespaces and you can learn them on need, but before
Shellman Structure iv

that, you need to know a few things about shell scripting. I will try my best to keep Basics section
short and simple so you can move fast to desired namespaces.
Shell Scripting Basics
Comments
In shell scripts, comments start with #. The exception is shebang which you see as the first line of
scripts.

1 # This is a comment

shebang
This is the first line of most of shell scripts. You may see different versions of it:

• #!/usr/bin/sh
• #!/usr/bin/bash
• #!/usr/bin/env bash
• …

This line tells the operating system which script engine should be used to run the script. Usually you
don’t need to change the default value Shellman provides:

1 #!/usr/bin/env bash

This is available via shebang | bash snippet.


If a shell script doesn’t contain shebang then whoever gonna execute such an script needs to specify
the script engine manually and pass the script as an argument to it:

1 bash test.sh
Shell Scripting Basics 2

Run a Shell Script


Shell script files by convention has .sh file extension⁷. To run a shell script (test.sh for example)
from terminal you have two options:

• Run it with bash command (pass file path to bash):


1. bash test.sh
• Give it execute permission and run it directly (prefix file name with a ./ without space):
1. chmod +x test.sh
2. ./test.sh

Run a Command from Shell Script


To run a command from your script just write it in your script as you do in terminal:

1 #!/usr/bin/env bash
2
3 rm some-file

If the command needs root⁸ privileges (in Windows it is known as Admin), prefix the command
with sudo:

1 #!/usr/bin/env bash
2
3 sudo rm some-file

If you need the result of the executed command refer to command substitution.

Multiline Command
A single command can be written in multiple lines if each line ends in a backslash.

⁷In Linux unlike Windows, file extensions has no special meaning to operating system but still you can use them to remember which file
type you are dealing with. vscode uses file extensions to recognize file types (.sh for Shellscript)
⁸In Linux/Unix systems, root is the most privileged user (same as Administrator in Windows).
Shell Scripting Basics 3

1 #!/usr/bin/env bash
2
3 curl --request GET -sL \
4 --user-agent 'Shellman' \
5 --cookie 'key=value' \
6 --url 'http://example.com'

Above script is the same as:

1 #!/usr/bin/env bash
2
3 curl --request GET -sL --user-agent 'Shellman' --cookie 'key=value' --url 'http://ex\
4 ample.com'

You can write multiple commands in a single line and separate them by semicolon (;).

1 #!/usr/bin/env bash
2
3 var1=2; var2=3; var3="hello"

Variables
There is a simple difference between when you define a variable and when use its value. In latter
case you need to prefix a $ to the variable name (also you can write ${variable}).
Define a variable named firstName and set its value to Remisa:

1 firstName=Remisa

Variable Assignment Rule


Spaces are not allowed over equal sign = in variable assignment.

Now if we want to read our variable value and print in on screen with echo command we can write:
Shell Scripting Basics 4

1 firstName=Remisa
2 echo $firstName
3 # or
4 echo ${firstName}

Variable Access Rule


To access a variable value prefix it with $

Variables are case sensitive (like Linux filesystem):

1 #!/usr/bin/env bash
2
3 var=1
4 Var=2
5
6 echo "$var" # 1
7 echo "$Var" # 2

As you may guessed in assignment rule, space has a special meaning in shell scripting. With
space over = shell assumes variable is a command and = and variable value are parameters to that
command.

1 firstName = Remisa
2 # firstName: command not found

We should take care of where a space may appear. For example our variable value may contains
space:

1 fullName=Remisa Yousefvand

Now when we want to use fullName value we put a $ before it and use $fullName instead. But it
contains space and we need to take care of that. To do so, simply surround wherever whitespace
may appear in "":

1 fullName="Remisa Yousefvand"
2 echo "$fullName"

Consider you want to delete a file named some file.txt and you have save its name in a variable
like:
Shell Scripting Basics 5

1 fileName="some file.txt"
2 rm $fileName

With above script instead of deleting some file.txt you are telling rm to delete two files named
some and file.txt and you will get an error (No such file or directory).

Handling whitespace in variables


Always surround variables in "" when accessing their values if they may contain white
space(s).

To concat multiple variables put them in "" in desired order:

1 a="Hello"
2 b="world"
3 c="!"
4 echo "$a $b$c"
5 # Hello world!

The whitespace between $a and $b is the whitespace between Hello and world in the output.
If you need adding more characters between variables then use "${variable}" syntax (this syntax
is recommended by many sources as the default syntax):

1 a="abc"
2 b="def"
3 c="ghi"
4 echo "${a}a ${b}b ${c}c"
5 # abca defb ghic

If we want to assign a variable if and only if it has no value currently, then we can use default value
snippet:

1 #!/usr/bin/env bash
2
3 : "${variable:=default}"

In above example variable is set only if it is empty. We will use this snippet later after argument
parsing to assign default values to variables when they are not passed (optional parameters) to script.
Shell Scripting Basics 6

Variable Types
There are three variable types in shell scripting: String, Integer and Array. Most of the time you
only need String. Even when working with numbers they are strings you pass to commands which
take care of converting those strings to numbers, do calculations, and return String back to you.
Although you can define variables using declare keyword, in this book we define variables literally.

1 # Number or Sting:
2 var1=1234
3 var2=12.56
4 var3="some text" # use double quote when there is a space in string
5
6 # Array:
7 myArray=("one" "two" "three")
8 # or
9 myArray2=(
10 "four"
11 "five"
12 "six"
13 )
14
15 echo "$var1" # 1234
16 echo "$var2" # 12.56
17 echo "$var3" # some text
18 echo "${myArray[@]}" # one two three
19 echo "${myArray2[@]}" # four five six

Function
Function in shell script is not what you expect from a function in other languages. They are like
commands defined in your script just like echo and ls. To define a function named myfunc simply
(there is a func snippet for that):

1 #!/usr/bin/env bash
2 function myfunc () {
3 echo "$1"
4 }

Function definition should precedes its usage. function keyword is optional and can be omitted:
Shell Scripting Basics 7

1 #!/usr/bin/env bash
2 myfunc () {
3 echo "$1"
4 }

To access function arguments we use $1, $2, $3… or access all of them at once through an array:

1 #!/usr/bin/env bash
2 function myfunc () {
3 arguments=("$@")
4 # arguments is the array variable containing all function parameters
5 }

If you need to return some value from a function use echo. There is a return keyword in bash but
you cannot use it for returning values from functions most of the time (unless your function return
an integer between 0 and 255) also it has its own meaning (0 for success and 1-255 for error codes).
If you want to terminate a function execution at some point use return (for example inside an if
statement).

1 #!/usr/bin/env bash
2 function myfunc () {
3 echo "this is the result"
4 # we don't need "return" here because function already reaches its end
5 }

On the caller side we capture this result with command substitution.

1 function myfunc () {
2 echo "this is the result"
3 }
4
5 result=`myfunc`
6 echo "$result"

For more function related operations see function snippets.

Commands

Command substitution
It is common practice to store the output of commands inside variables for further processing in
script. The process is known as command substitution and can be done in two syntaxes:
Shell Scripting Basics 8

1. output=`command`
2. output=$(command)

In most references method two is recommended because it is the only one that works with nested
command substitutions. For the sake of brevity and consistency, we will use method one (backtick)
in this book unless we need nested command substitutions (Also to be able to read and understand
shell scripts written by others).
To store results of ls command in a variable named output:

1 output=`ls` # store ls results in a variable named output


2 # same as: output=$(ls)
3
4 echo "$output" # print output value (ls result)

There is a more advance technique for using a command output as another command input, namely
piping (|), which is beyond the scope of this book (if you have functional programming background
you are already familiar with the idea).

Command success/failure check


It happens when you are interested to know if a previous command succeeded or failed. In Linux
every program returns a number to operating system on exit⁹. If the return value is zero, in means
no error happened and other values indicates command failure (1-255).

Command success/failure
Programs return 0 in case of success and non zero if failure occurs.

To check that, you can read last command return value via $?. There is a snippet at func namespace
for retrieving last command return value as func ret val:

1 echo "$?"

Shellman supports checking failure of last command via cmd namespace as cmd failure check
snippet:

⁹This number is between 0 and 255 (one byte). If you have ever programmed in C/C++, you may noticed a return 0 as a default behavior,
that is the code your program is returning to OS, here 0 as success.
Shell Scripting Basics 9

1 # following command will fail due to lack of permission


2 touch /not-enough-permission-to-create-file

touch command creates an empty file.


We are trying to create the empty file not-enough-permission-to-create-file at the root of your
file system (/). Without sudo normally (unless user is root) this command will fail due to lack of
enough permissions.

1 touch /not-enough-permission-to-create-file
2
3 # check last command (touch) success/failure
4 if [[ $? != 0 ]]; then
5 echo "command failed"
6 fi

To check success, use cmd success check snippet from cmd namespace:

1 echo "Hello World!"


2
3 # check last command (echo) success/failure
4 if [[ $? == 0 ]]; then
5 echo command succeed
6 fi

Check command exit code immediately after that command or you may get wrong result:

1 #!/usr/bin/env bash
2
3 touch /not-enough-permission-to-create-file
4
5 echo "checking operation..."
6
7 # check last command (echo) success/failure
8 if [[ $? != 0 ]]; then
9 echo "command failed"
10 fi

In above example your if statement won’t print the command failed message since last command
is echo and not touch.

Exit
It is a good practice to inform script caller (in case other scripts use yours) about success or failure
of your script. To indicate success:
Shell Scripting Basics 10

1 exit 0

And if an error happens use an exit code. Document exit codes at the top of of your script:

1 exit 5 # documented as "no internet connection"

Argument parsing
By convention most Linux commands/programs supports a long and short version for the same
flag/switch. Short version is usually the first letter of the long version (unless it is taken, like adding
version to following list). Some examples:

short long
-v –verbose
-s –silent
-f –force
-o –output

You may want to support different switches/flags by your script and act differently based on them.
Suppose your script name is backup.sh. With supporting flags someone can run it as:

1 ./backup.sh -v

So your script works different with -v. For example you print verbose information. We need to know
if user has run our script with or without -v flag. Shellman makes it easy for you, keep reading.
If your script supports switches, it means user is passing some information to your script via that
switch. For example where to save the backup in our example:

1 ./backup.sh -o ~/my_backups

In above code we are telling the script to save the output in ∼/my_backups¹⁰ directory. Here -o is a
switch which takes one parameter (a path).

Flag vs Switch
Flag is used for boolean values and its presence means True while Switch accepts
argument(s).

Shellman has a parse args snippet. It looks like this:


¹⁰∼ is a shorthand for current user, home directory, which usually is /home/username. This path is also accessible via $HOME global variable.
Shell Scripting Basics 11

1 POSITIONAL=()
2 while [[ $# > 0 ]]; do # while arguments count > 0
3 case "$1" in
4 -f|--flag)
5 echo flag: $1
6 shift # shift once since flags have no values
7 ;;
8 -s|--switch)
9 echo switch $1 with value: $2
10 shift 2 # shift twice to bypass switch and its value
11 ;;
12 *) # unknown flag/switch
13 POSITIONAL+=("$1")
14 shift
15 ;;
16 esac # end of case. "case" word in reverse!
17 done
18
19 set -- "${POSITIONAL[@]}" # restore positional params

The while loop keeps looping until there is no more arguments to process. Although the passed
arguments to your script would not disappear themselves, we trim them from left using shift
command. So if your script is executed like:

1 ./greet.sh -m --name Remisa

Input arguments are -m --name Remisa. After a shift they become --name Remisa and so on. So if
you need to process a switch with two arguments shift 3.
This snippet will take care of Flags and Switches of your script. For implementing your own flag(s)
replace -f|--flag with desired flag, i.e. -v|--verbose and on the next lines (before shift) do
whatever you need. It is recommended to define a variable and set it here to keep track of the
flag or store the value of switch parameter(s):

1 -v|--verbose)
2 verbose=true

Repeat above procedure for more flags.


To implement a switch like -o/--output:
Shell Scripting Basics 12

1 -o|--output)
2 outputPath=$2

In above example we are saving the switch value in outputPath for using later. We refer to first
switch parameter with $2 and the second with $3 and so on because the $1 refers to the switch itself.
Then shift properly.
Repeat above procedure for more switches.

Argument Parsing Exercise


Write a shell script to greet. Script receives the name via --name or -n switch to print good
night name and if -m flag is set, it should print good morning name. name is what value passed
to script via --name flag. If --name or -n is not passed default value would be everyone.
Example outputs:

1 ./greet.sh
2 # good night everyone
3
4 ./greet.sh -m
5 # good morning everyone
6
7 ./greet.sh --name Remisa
8 # good night Remisa
9
10 ./greet.sh -n Remisa
11 # good night Remisa
12
13 ./greet.sh -m --name Remisa
14 # good morning Remisa
15
16 ./greet.sh -m -n Remisa
17 # good morning Remisa

For the answer check solutions section.


As you have noticed, first argument can be accessed via $1, second argument via $2…
And yes, $0 refers to script name itself at the time of execution.
Same is true inside the body of a function to access passed arguments to the function.
Shell Scripting Basics 13

Organizing your Shell Script


An organized script is easy to understand and maintain. Recommended structure of script.sh from
top to bottom is:

1. shebang (shebang | bash snippet)


2. summary (summary snippet)
3. handler functions region (if any, see event namespace)
4. event handlers region (if any, see event namespace)
5. animation frames region (if any, see animation namespace)
6. functions region
7. argument parsing
8. setting default variable values
9. rest of code (minimize it to function calls)

Usually you only need 1, 2, 6, 9 from above list. argument parsing and setting default variable values
can be done in reverse order. In that case create a variables region after summary and set default
values. Later if argument parsing overrides some of your variables (passed as flag/switch) the rest
of variables contain default values.
In summary you provide some information about script.

1 #!/usr/bin/env bash
2
3 # Title: test
4 # Description: a test script
5 # Author: Remisa <[email protected]>
6 # Date: 2019-01-06
7 # Version: 1.0.0
8
9 # Exit codes
10 # ==========
11 # 0 no error
12 # 1 script interrupted
13 # 2 error description

Event handling
If you need to run a set of specific tasks before your script exits or in case user terminates your script
(pressing CTRL + C) you need to assign a handler function to appropriate event. The problem with
Shell Scripting Basics 14

event handlers is we use functions to run if a certain event happens so before assigning an event to a
function we need to write the function. To capture events as soon as possible we need to assign event
handlers early in our script. Thats why I have separated functions into two parts, event handlers,
at the top of the script just before binding events to them and the rest of functions which are not
needed so early. See event namespace for more information.
Use region snippet to define a functions region and put all of your functions there. Remember you
need to define functions before you can use them. If function B calls function A, then function A
definition should precede definition of function B.

1 #!/usr/bin/env bash
2
3 # summary here
4
5 # >>>>>>>>>>>>>>>>>>>>>>>> functions >>>>>>>>>>>>>>>>>>>>>>>>
6
7 function greet() {
8 # access the argument via $1
9 echo "Hello $1"
10 }
11
12 # <<<<<<<<<<<<<<<<<<<<<<<< functions <<<<<<<<<<<<<<<<<<<<<<<<
13
14 greet "Shellman" # call the function and pass an argument

Double Quote vs Single Quote vs Backtick


Use double quotation where you have a variable that contains whitespace. Any variable inside a
double quotation will be replaced by its value:

1 var1="Hello World!"
2 echo "$var1" # Hello World!
3 # OR
4 echo "${var1}" # Hello World!

Double Quote
By default use Double Quote " when defining variable or trying to access a variable value.

Use single quotation where you need to define a variable that contains special characters. Anything
inside a single quotation will remain exact the same:
Shell Scripting Basics 15

1 var1="Hello World!"
2 echo "$var1" # Hello World!
3
4 var2='$var1'
5 echo "$var2" # $var1
6
7 var3='"&$*'
8 echo "$var3" # "&$*

backtick is used for command substitution

1 directoryList=`ls | xargs echo`


2 echo "$directoryList"

Sample scripts
Apart from some examples in this book there is a samples directory¹¹ in project repository which
contains the steps and reasoning behind writing some shell scripts using Shellman.
¹¹https://github.com/yousefvand/shellman/tree/master/samples
Namespaces
Namespaces are semantic categories to hold related items together. Folders play the same role in
keeping related files together on a file system.
There is a length limitation to namespaces in a snippet so some of them doesn’t exist under exact
namespace I write in this book. Fortunately they are few.
It happens when a single snippet is not enough to do the job and a function is needed. Such functions
available in Shellman. See lib for more information.
Namespaces 17

loop
Contains while, until and for. Actually for doesn’t have loop prefix so by typing loop you won’t
see it. That’s because of readability limitations so if you need any kind of for type for.

while
while condition.

For arithmetic comparison use (( )).

1 #!/usr/bin/env bash
2
3 a=3
4 while (( a > 0 )); do
5 echo "$a"
6 ((a--))
7 done
8 # 3
9 # 2
10 # 1

For string comparison use [ ].

1 #!/usr/bin/env bash
2
3 str="s"
4 while [ "$str" != "end" ]; do
5 echo "start"
6 str="end"
7 done
8 # start

Also loop infinite snippet is available:


Namespaces 18

1 #!/usr/bin/env bash
2
3 while true; do
4 echo "message every 2 seconds"
5 sleep 2s
6 done

until
until condition (opposite of while).

For arithmetic comparison use (( )).

1 #!/usr/bin/env bash
2
3 a=3
4 until (( a <= 0 )); do
5 echo "$a"
6 ((a--))
7 done
8 # 3
9 # 2
10 # 1

For string comparison use [ ].

1 #!/usr/bin/env bash
2
3 str="s"
4 until [ "$str" == "end" ]; do
5 echo "start"
6 str="end"
7 done
8 # start

for i
for loop.
Namespaces 19

1 #!/usr/bin/env bash
2
3 for((i=0;i<5;i++)); do
4 echo "$i"
5 done
6 # 0
7 # 1
8 # 2
9 # 3
10 # 4

for i j
Nested for loop.

1 #!/usr/bin/env bash
2
3 for((i=0;i<3;i++)); do
4 for((j=0;j<2;j++)); do
5 echo "$i, $j"
6 done
7 done
8 # 0, 0
9 # 0, 1
10 # 1, 0
11 # 1, 1
12 # 2, 0
13 # 2, 1

for in
Iterate over ranges. Range can be numerical or alphabetical and can be defined as {start..end}.
Numerical range:
Namespaces 20

1 #!/usr/bin/env bash
2
3 for item in {1..5}; do
4 echo "$item"
5 done
6 # 1
7 # 2
8 # 3
9 # 4
10 # 5

alphabetical range:

1 #!/usr/bin/env bash
2
3 for item in {A..D}; do
4 echo "$item"
5 done
6 # A
7 # B
8 # C
9 # D

for in column
Sometimes output is arranged in multiple columns while we are interested in one or few of them.
For example output of docker images command:

REPOSITORY TAG IMAGE ID CREATED SIZE


sonatype/nexus3 3.13.0 777b20c20405 3 months ago 505MB
sonatype/nexus3 latest 777b20c20405 3 months ago 505MB
busybox glibc c041448940c8 4 months ago 4.42MB
busybox latest c041448940c8 4 months ago 4.42MB

What if we are just interested in column one?

1 #!/usr/bin/env bash
2
3 for col in `docker images | awk '{ print $1}'`; do
4 echo "$col"
5 done

Output of above script is:


Namespaces 21

1 REPOSITORY
2 sonatype/nexus3
3 sonatype/nexus3
4 busybox
5 busybox

If you need column two you can pipe (|) output of docker images to awk '{ print $2}':

1 #!/usr/bin/env bash
2
3 for col in `docker images | awk '{ print $2}'`; do
4 echo "$col"
5 done

Output would be:

1 TAG
2 3.13.0
3 latest
4 glibc
5 latest

logic
You can find logical related commands here under if namespace.

if
if, else condition.

For arithmetic comparison use (( )).


Namespaces 22

1 #!/usr/bin/env bash
2
3 var1=32
4 var2=33
5
6 if (( $var1 == $var2 )); then
7 echo "equal"
8 elif (( $var1 >= $var2 )); then
9 echo "bigger"
10 else
11 echo "smaller"
12 fi
13 # smaller

For string comparison use [ ].


elif part can be repeated as much as necessary.

1 #!/usr/bin/env bash
2
3 str1="bye"
4 str2="hello"
5 str3="bye"
6
7 if [ "$str1" = "$str2" ]; then
8 echo "1 = 2"
9 elif [ "$str1" = "$str3" ]; then
10 echo "1 = 3"
11 elif [ "$str2" = "$str3" ]; then
12 echo "2 = 3"
13 else
14 echo "no equal pair"
15 fi
16 # 1 = 3

Simpler forms of if:


Namespaces 23

1 #!/usr/bin/env bash
2
3 str1="bye"
4 str2="hello"
5
6 if [ "$str1" = "$str2" ]; then
7 echo "equal"
8 fi

or if/else:

1 #!/usr/bin/env bash
2
3 str1="bye"
4 str2="hello"
5
6 if [ "$str1" = "$str2" ]; then
7 echo "equal"
8 else
9 echo "NOT equal"
10 fi
11 # NOT equal

iff
If condition is true then run command (short circuit).
For arithmetic comparison use (( )).

1 #!/usr/bin/env bash
2
3 var=5
4 (( var > 3 )) && echo "greater than 3"
5 # greater than 3

For string comparison use [ ].


Namespaces 24

1 #!/usr/bin/env bash
2
3 var="hi"
4 [ "$var" = "hi" ] && echo "hi"
5 # hi

iff not
If condition is false then run command (short circuit).
For arithmetic comparison use (( )).

1 #!/usr/bin/env bash
2
3 var=5
4 (( var > 8 )) || echo "less than 8"
5 # less than 8

For string comparison use [ ].

1 #!/usr/bin/env bash
2
3 var="hi"
4 [ "$var" = "bye" ] || echo "hi"
5 # hi

if int =
Check if two integers are equal.

1 #!/usr/bin/env bash
2
3 int1=67
4 int2=67
5
6 if (( int1 == int2 )); then
7 echo equal
8 fi

if int !=
Check if two integers are not equal.
Namespaces 25

1 #!/usr/bin/env bash
2
3 int1=12
4 int2=13
5
6 if (( int1 != int2 )); then
7 echo not equal
8 fi

if int <
Check if the first integer is smaller than the second.

1 #!/usr/bin/env bash
2
3 int1=12
4 int2=13
5
6 if (( int1 < int2 )); then
7 echo lesser
8 fi

if int <=
Check if the first integer is smaller or equal to the second one.

1 #!/usr/bin/env bash
2
3 int1=12
4 int2=13
5
6 if (( int1 <= int2 )); then
7 echo less or equal
8 fi

if int >
Check if the first integer is greater than the second.
Namespaces 26

1 #!/usr/bin/env bash
2
3 int1=15
4 int2=13
5
6 if (( int1 > int2 )); then
7 echo greater
8 fi

if int >=
Check if the first integer is greater or equal to the second one.

1 #!/usr/bin/env bash
2
3 int1=12
4 int2=13
5
6 if (( int1 >= int2 )); then
7 echo greater or equal
8 fi

if string empty
Check if string is empty.

1 #!/usr/bin/env bash
2
3 str=""
4 if [ -z "$str" ]; then
5 echo "Empty string"
6 fi
7 # Empty string

if string not empty


Check if string is not empty.
Namespaces 27

1 #!/usr/bin/env bash
2
3 str="a"
4 if [ -n "$str" ]; then
5 echo "String is not empty"
6 fi
7 # String is not empty

string equal | if string =


Check if strings are equal.

1 #!/usr/bin/env bash
2
3 str1="hello"
4 str2="hello"
5 if [ "$str1" = "$str2" ]; then
6 echo "equal"
7 fi
8 # equal

string not equal | if string !=


Check if strings are not equal.

1 #!/usr/bin/env bash
2
3 str1="hi"
4 str2="hello"
5 if [ "$str1" != "$str2" ]; then
6 echo "not equal"
7 fi
8 # not equal

if string contains
Check if string contains given substring.
Namespaces 28

1 #!/usr/bin/env bash
2
3 str="hello world!"
4
5 if [[ "$str" = *world* ]]; then
6 echo contains world
7 fi
8 # contains world

if directory exists
Check if given path is a directory.

1 #!/usr/bin/env bash
2
3 if [ -d ~/backup ]; then
4 echo "backup exists"
5 fi

if cmd exists
Read cmd

if exists
If path exists.

1 #!/usr/bin/env bash
2
3 path=~/.bashrc
4 if [ -e "$path" ]; then
5 echo exists
6 fi

if file exists
If given file exists.
Namespaces 29

1 #!/usr/bin/env bash
2
3 filepath="/path/to/file"
4
5 file if [ -f "$filepath" ]; then
6 echo file exists
7 fi

if file =
Check if two files are equal.

1 #!/usr/bin/env bash
2
3 file1=~/some_file
4 file2=~/another_file
5
6 if [ "$file1" -ef "$file2" ]; then
7 echo files are equal
8 fi

if file executable
Check if file is executable.

1 #!/usr/bin/env bash
2
3 if [ -x /bin/ls ]; then
4 echo file is executable
5 fi

if file link
If given path is a symbolic link.
Namespaces 30

1 #!/usr/bin/env bash
2
3 if [ -h /vmlinuz ]; then
4 echo symbolic link
5 fi

if file newer
Check if first file is newer than the second.

1 #!/usr/bin/env bash
2
3 file1=~/.bashrc
4 file2=~/.profile
5
6 if [ "$file1" -nt "$file2" ]; then
7 echo file1 is newer than file2
8 fi

if file not empty


Check if file is not empty (file size > 0).

1 #!/usr/bin/env bash
2
3 if [ -s ~/.profile ]; then
4 echo file not empty
5 fi

if file older
Check if first file is older than the second.
Namespaces 31

1 #!/usr/bin/env bash
2
3 file1=~/.bashrc
4 file2=~/.profile
5
6 if [ "$file1" -ot "$file2" ]; then
7 echo file1 is older than file2
8 fi

if file readable
Check if file is readable.

1 #!/usr/bin/env bash
2
3 if [ -r ~/.profile ]; then
4 echo file is readable
5 fi

if file writable
Check if file is writable.

1 #!/usr/bin/env bash
2
3 if [ -w ~/.profile ]; then
4 echo file is writable
5 fi

function
Contains function related operations available through func namespace. A function can return a
number between 0 to 255 which can be retrieved through $? (available as func ret val snippet).

func
Define a function to be called later. Function definition must precede its usage.
Namespaces 32

1 #!/usr/bin/env bash
2
3 function myFunction () {
4 echo "$1"
5 echo "$2"
6 }
7
8 myFunction "some argument" "another argument"
9 # some argument
10 # another argument

func args
Access to function arguments.

1 #!/usr/bin/env bash
2
3 function myFunction () {
4 echo "$@"
5 }
6
7 myFunction "some argument" "another argument"
8 # some argument another argument

func args count


Number of function arguments.

1 #!/usr/bin/env bash
2
3 function myFunction () {
4 echo $#
5 }
6
7 myFunction "some argument" "another argument"
8 # 2

func ret val


Check the value last function call has returned (0-255). By convention, zero is returned if no error
occurs, otherwise a non-zero value is returned.
Namespaces 33

1 #!/usr/bin/env bash
2
3 function test () {
4 echo "$1"
5 return 25
6 }
7
8 test "return value"
9 echo "$?"
10 # return value
11 # 25

string
Contains String related operations.

string concat
concatenates two strings

1 #!/usr/bin/env bash
2
3 str1="a"
4 str2="b"
5 str="${str1}y${str2}z"
6 echo "$str" # aybz

string contains | if string contains


Checks if a String contains another String (substring).
Namespaces 34

1 #!/usr/bin/env bash
2
3 var="hello world!"
4
5 if [[ "$var" = *world* ]]; then
6 echo "substring found"
7 else
8 echo "substring NOT found"
9 fi

string equal | if string =


Checks if two Strings are the same.

1 #!/usr/bin/env bash
2
3 string1='This is a string!'
4 string2='This is a string!'
5
6 if [ "$string1" = "$string2" ]; then
7 echo 'Strings are equal'
8 fi

string not equal | if string !=


Checks if two strings are not equal.

1 #!/usr/bin/env bash
2
3 str1="shellman"
4 str2="shellmen"
5 if [ "$str1" != "$str2" ]; then
6 echo "Strings are NOT equal"
7 fi

string indexOf
Returns index of substring inside a string.
Namespaces 35

1 #!/usr/bin/env bash
2
3 myString="Hello World!"
4 temp=${myString%%"or"*} && indexOf=`echo ${myString%%"or"*} | echo ${#temp}`
5 echo $indexOf # 7

if string empty
Check if variable is an empty string.

1 #!/usr/bin/env bash
2
3 var=""
4 if [ -z "$var" ]; then
5 echo "Variable is empty string."
6 fi
7 # Variable is empty string.

if string not empty


Check if variable is not an empty string.

1 #!/usr/bin/env bash
2
3 var="something"
4 if [ -n "$var" ]; then
5 echo "Variable is not an empty string."
6 fi
7 # Variable is not an empty string.

string length
Returns length of given string.
Namespaces 36

1 #!/usr/bin/env bash
2
3 var="abcdefg"
4 length=${#var}
5 echo "$length"

string random
Generates a random string from specified characters with desired length.

1 randomString=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 16 ; echo '')


2 echo "$randomString"

In above example random string is built up from characters A-Z, a-z and 0-9 and length of it would
be 16 characters.

string replace
Replace a substring with given string in another string.

1 #!/usr/bin/env bash
2
3 str1="Hello World!"
4 replaced=`echo -e "${str1}" | sed -e 's/World/Everyone/g'`
5 echo "$replaced" # Hello Everyone!

string reverse
Reverse given string.

1 #!/usr/bin/env bash
2
3 str1="abcd"
4 reversed=`echo -e "${str1}" | rev`
5 echo "$reversed" # dcba

string substring
Returns a substring from given string starting at index and with the length of length.
Namespaces 37

1 #!/usr/bin/env bash
2
3 str1="abcdefg"
4 substring=`echo -e "${str1:2:3}"`
5 echo "$substring" # cde

In above example we want a substring starting at index 2 to the length of 3. In abcdefg index 2 is c
(index starts at zero) and length of 3 will end up cde.

string substring count | string substring frequency


Finds the frequency of a substring in a string (may need character escaping).

1 #!/usr/bin/env bash
2
3 frequency=`sed -E 's/(.)/\1\n/g' <<<"a!bc!def!" | grep -c "!"`
4 echo "${frequency}" # 3

string toLower
Returns lowercase of given string.

1 #!/usr/bin/env bash
2
3 str1="AbCdE"
4 toLower=`echo -e "${str1}" | tr '[:upper:]' '[:lower:]'`
5 echo "$toLower" # abcde

string toUpper
Returns uppercase of given string.

1 #!/usr/bin/env bash
2
3 str1="AbCdE"
4 toLower=`echo -e "${str1}" | tr '[:upper:]' '[:lower:]'`
5 echo "$toLower" # abcde

string trim
Removes leading and trailing whitespace(s).
Namespaces 38

1 #!/usr/bin/env bash
2
3 str1=" result "
4 result=`echo -e "${str1}" | sed -e 's/^[[:space:]]*//' | sed -e 's/[[:space:]]*$//'`
5 echo "Variable $result contains no leading and trailing space as you see"
6 # Variable result contains no leading and trailing space as you see

string trim all


Removes all whitespace(s) from given string (leading, inside, trailing).

1 #!/usr/bin/env bash
2
3 str1=" ab c de "
4 result=`echo -e "${str1}" | tr -d '[[:space:]]'`
5 echo "All whitespaces are removed from $result as you see"
6 # All whitespaces are removed from abcde as you see

string trim left


Removes all whitespace(s) from left of given string (leading).

1 #!/usr/bin/env bash
2
3 str1=" whitespace on left"
4 result=`echo -e "${str1}" | sed -e 's/^[[:space:]]*//'`
5 echo "There is no $result as you see"
6 # There is no whitespace on left as you see

string trim right


Removes all whitespace(s) from right of given string (trailing).
Namespaces 39

1 #!/usr/bin/env bash
2
3 str1="whitespace on right "
4 result=`echo -e "${str1}" | sed -e 's/[[:space:]]*$//'`
5 echo "There is no $result as you see"
6 # There is no whitespace on right as you see

array
Contains Array related operations.

array declare
Declare a literal array.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4
5 for item in ${myArray[@]}; do
6 echo "$item"
7 done
8
9 # Alice
10 # Bob
11 # Eve

array add | array push


Add a new item to the array.
Namespaces 40

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 myArray+=("Shellman")
5
6 for item in ${myArray[@]}; do
7 echo "$item"
8 done
9
10 # Alice
11 # Bob
12 # Eve
13 # Shellman

array all
All items of array.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 echo ${myArray[@]} # Alice Bob Eve

array at index
Returns item Nth from array (N = index).

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve" "Shellman")
4 echo ${myArray[2]} # Eve

array concat
Returns an array made of concatenation of two given arrays.
Namespaces 41

1 #!/usr/bin/env bash
2
3 array1=("Alice" "Bob" "Eve")
4 array2=("1" "2" "3")
5 newArray=("${array1[@]}" "${array2[@]}")
6 echo ${newArray[@]} # Alice Bob Eve 1 2 3

array delete
Delete entire array.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 unset myArray
5 echo ${myArray[@]}
6 #

array delete at
Delete Nth item in array (N = index)

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 unset myArray[1]
5 echo ${myArray[@]} # Alice Eve

array filter
Filter elements of an array based on given pattern.

1 #!/usr/bin/env bash
2
3 myArray=('Alice' '22' 'Bob' '16' 'Eve')
4 filtered=(`for i in ${myArray[@]} ; do echo $i; done | grep [0-9]`)
5 echo ${filtered[@]} # 22 16
Namespaces 42

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 echo ${myArray[@]/e/} # Alice Eve

array iterate | array forEach


Iterate over array items.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4
5 for item in ${myArray[@]}; do
6 echo "$item"
7 done
8
9 # Alice
10 # Bob
11 # Eve

array length
Returns length of array.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 echo ${#myArray[@]} # 3

array replace
Find and replace items in array based on regex.
Namespaces 43

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 echo ${myArray[@]//e/9} # Alic9 Bob Ev9

array slice | array range


Return items from index up to the count.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve" "Shellman" "Remisa")
4 echo ${myArray[@]:1:3} # Bob Eve Shellman

In above example we are interested in 3 items of array starting at index 1 (arrays are zero base
indexed)

array set element


Set element given value as Nth element.

1 #!/usr/bin/env bash
2
3 myArray=("Alice" "Bob" "Eve")
4 myArray[1]="Shellman"
5 echo ${myArray[@]} # Alice Shellman Eve

cmd
Contains command execution related operations.

cmd
To run a command and use the returned value is named command substitution.
Namespaces 44

1 #!/usr/bin/env bash
2
3 response=`curl -s http://example.com`
4 echo "$response"

In above example using curl we retrieve the content of http://example.com and store it in response
variable (-s flag tells curl to work in silent mode).

cmd success check


Check if last command has succeeded.

1 #!/usr/bin/env bash
2
3 ls # this command will succeed
4
5 if [[ $? == 0 ]]; then
6 echo "command succeeded"
7 else
8 echo "command failed"
9 fi
10 # command succeeded

cmd failure check


Check if last command has failed.

1 #!/usr/bin/env bash
2
3 touch /file.txt # this command will fail without sudo
4
5 if [[ $? == 0 ]]; then
6 echo "command succeeded"
7 else
8 echo "command failed"
9 fi
10 # command failed

cmd nice
Run a command with modified scheduling priority. Niceness values range from -20 (highest priority)
to 19 (lowest priority) and default value is 0.
Namespaces 45

1 #!/usr/bin/env bash
2
3 sudo nice -n 19 cp ~/file ~/tmp

In above example we are copying a file from home to tmp folder, and schedule minimum CPU time
to cp.

cmd renice
Change a running process priority. Niceness values range from -20 (highest priority) to 19 (lowest
priority) and default value is 0.

1 #!/usr/bin/env bash
2
3 for p in $(pidof "chrome"); do sudo renice -n -5 -p "$p"; done

In above example we are changing priority of chrome process and its child processes to higher than
normal.

if cmd exists
Check if a desired command exists (program is installed).

1 #!/usr/bin/env bash
2
3 if [ `command -v docker` ]; then
4 echo "docker is installed"
5 else
6 echo "docker is NOT installed"
7 fi

In above example we are checking if docker program is available on the system.

math
Contains Math related operations. Math functions are available under fn math ... namespace.

math % (modulus)
Given two variables, returns reminder of dividing the first variable to the second.
Namespaces 46

1 #!/usr/bin/env bash
2
3 var1=17
4 var2=5
5 reminder=$((var1 % var2))
6 echo "$reminder" # 2

math %= (modulus assign)


Given two variables, calculates reminder of dividing the first variable to the second and assigns the
result to the first variable.

1 #!/usr/bin/env bash
2
3 var1=13
4 var2=5
5 ((var1 %= var2))
6 echo "$var1" # 3

math * (multiply)
Given two variables, returns product of them.

1 #!/usr/bin/env bash
2
3 var1=3
4 var2=4
5 result=$((var1 * var2))
6 echo "$result" # 12

math *= (multiply assign)


Given two variables, calculates product of them and assigns the result to the first variable.
Namespaces 47

1 #!/usr/bin/env bash
2
3 var1=2
4 var2=3
5 ((var1 *= var2))
6 echo "$var1" # 6

Factorial
Write a function which gets a number N and prints N!.

For the answer refer to Solutions section, factorial.

math + (add)
Given two variables, returns sum of them.

1 #!/usr/bin/env bash
2
3 var1=2
4 var2=3
5 result=$((var1 + var2))
6 echo "$result" # 5

math ++ (increase)
Given a variables, adds one to it.

1 #!/usr/bin/env bash
2
3 var=7
4 echo $((++var)) # 8

math += (add assign)


Given two variables, calculates sum of them and assigns the result to the first variable.
Namespaces 48

1 #!/usr/bin/env bash
2
3 var1=2
4 var2=3
5 ((var1 += var2))
6 echo "$var1" # 5

math - (subtract)
Given two variables, returns first minus second.

1 #!/usr/bin/env bash
2
3 var1=7
4 var2=5
5 result=$((var1 - var2))
6 echo "$result" # 2

math – (decrease)
Given a variable, subtracts one from it.

1 #!/usr/bin/env bash
2
3 var=8
4 echo $((--var)) # 7

math -= (subtract assign)


Given two variables, calculates first variable minus the second and assigns the result to the first
variable.

1 #!/usr/bin/env bash
2
3 var1=19
4 var2=15
5 ((var1 -= var2))
6 echo "$var1" # 4

math / (divide)
Given two variables, returns first divided by the second.
Namespaces 49

1 #!/usr/bin/env bash
2
3 var1=12
4 var2=4
5 result=$((var1 / var2))
6 echo "$result" # 3

math /= (divide assign)


Given two variables, divides first variable by second and assigns the result to the first.

1 #!/usr/bin/env bash
2
3 var1=12
4 var2=4
5 ((var1 /= var2))
6 echo "$var1" # 3

math 0.00 (precision)


Math operations with x decimal point precision.
Multiply example:

1 #!/usr/bin/env bash
2
3 var1="2.13"
4 var2=""2
5 result=`echo "scale=2;($var1 * $var2)" | bc`
6 echo "$result" # 4.26

Division example:

1 #!/usr/bin/env bash
2
3 var1=7
4 var2=2
5 result=`echo "scale=2;($var1 / $var2)" | bc`
6 echo "$result" # 3.50

math ^ (power)
Exponentiate base to the power.
Namespaces 50

1 #!/usr/bin/env bash
2
3 echo $((2 ** 4)) # 16
4 echo $((3 ** 3)) # 27

math √ (square root)


Returns square root of given number up to given precision.
Calculate square root of 2 up to 7 decimal points.

1 #!/usr/bin/env bash
2
3 var=2
4 result=`echo "scale=7;sqrt($var)" | bc`
5 echo "$result" # 1.4142135

math random
Generate random number between min and max

1 #!/usr/bin/env bash
2
3 echo $((5000 + RANDOM % $((65535-5000)))) # 27502

math constants
Some useful math constants.

• Ï€ = 3.14159265358979323846264338327950288
• e = 2.71828182845904523536028747135266249
• ð�›¾ = 0.57721566490153286060651209008240243
• Ω = 0.56714329040978387299996866221035554
• Ï• = 1.61803398874989484820458683436563811

color
Write text in color. color namespace contains commands to write in different foreground colors. To
write in color we use tput setaf command followed by color code. Here is color code table:
Namespaces 51

Color Code Snippet


Black 0 color black
Red 1 color red
Green 2 color green
Yellow 3 color yellow
Blue 4 color blue
Magenta 5 color magenta
Cyan 6 color cyan
White 7 color white

To set foreground color to red we use tput setaf 1 command and after some output we use tput
sgr0 command to set everything to default. You don’t need to memorize codes, there is a snippet for
every color. So for writing hello world in red, use color red snippet:

1 #!/usr/bin/env bash
2
3 echo `tput setaf 1`hello world`tput sgr0`

Colorful Text
Write a shell script that prints Hello World! in all 8 colors using a for loop.

For the answer refer to Solutions section, colorful text.

format
Write text in italic, bold, dim or reverse contrast.

formated text

format bold
Write in bold.
Namespaces 52

1 #!/usr/bin/env bash
2
3 echo `tput bold`bold text`tput sgr0`

format italic
Write in italic.

1 #!/usr/bin/env bash
2
3 echo `tput sitm`italic text`tput sgr0`

format dim
Write dim text.

1 #!/usr/bin/env bash
2
3 echo `tput dim`dimmed text`tput sgr0`

format reverse
Write text in reverse contrast.

1 #!/usr/bin/env bash
2
3 echo `tput rev`reversed text`tput sgr0`

date
Contains Date related operations.

date now short


Short version of current system date.
Namespaces 53

1 #!/usr/bin/env bash
2
3 dateShort=`date -I`
4 echo "$dateShort" # 2019-01-06

date now UTC


Returns current system time in Coordinated Universal Time format.

1 #!/usr/bin/env bash
2
3 dateUTC=`date -u`
4 echo "$dateUTC" # Sunday, January 06, 2019

date now year


Current year.

1 #!/usr/bin/env bash
2
3 year=`date +%Y`
4 echo "$year" # 2019

date now monthNumber


Current month number.

1 #!/usr/bin/env bash
2
3 monthNumber=`date +%m`
4 echo "$monthNumber" # 01

date now monthName


Current month name.
Namespaces 54

1 #!/usr/bin/env bash
2
3 monthName=`date +%B` # %B for full month name, %b for abbreviated month name
4 echo "$monthName" # January

date now dayOfMonth


Current day of month.

1 #!/usr/bin/env bash
2
3 dayOfMonth=`date +%d`
4 echo "$dayOfMonth" # 06

date now dayOfWeek


Current weekday name.

1 #!/usr/bin/env bash
2
3 dayOfWeek=`date +%A` # %A for full weekday name, %a for abbreviated weekday name
4 echo "$dayOfWeek" # Sunday

date now dayOfYear


Current day of year (1-366).

1 #!/usr/bin/env bash
2
3 dayOfYear=`date +%j`
4 echo "$dayOfYear" # 006

time
Contains Time related operations.

time now local


Current local time.
Namespaces 55

1 #!/usr/bin/env bash
2
3 timeNowLocal=`date +%R` # %R for 24 hrs
4 echo "$timeNowLocal" # 13:23
5
6 timeNowLocal=`date +%r` # %r for 12 hrs
7 echo "$timeNowLocal" # 01:23:45

time now UTC


Current UTC time.

1 #!/usr/bin/env bash
2
3 timeNowUTC=`date -u +%R`
4 echo "$timeNowUTC" # 12:56

time seconds epoch


Seconds from 01-01-1970 00:00.

1 #!/usr/bin/env bash
2
3 timeNowSecondsEpoch=`date +%s`
4 echo "$timeNowSecondsEpoch" # 1545223678

file
Contains File related operations. For logical operations about file like if a given file is writable see
logic section.

file delete | file remove


Delete given file.
Namespaces 56

1 #!/usr/bin/env bash
2
3 rm -f ~/test.txt

In above example test.txt will be deleted from home.

file find
Find files or directories based on criteria in given path.

1 #!/usr/bin/env bash
2
3 result=`find ~ -maxdepth 5 -type f -name "*.txt"`
4 echo "$result"

In above example all files (-type f) with txt extension in home (∼) path up to 5 level of depth will
be found. To search for directories use -type d.

file search | search in files | find in files


Find files which contain the search criteria.

1 #!/usr/bin/env bash
2
3 result=`find ~ -maxdepth 1 -type f -exec grep "ls" {} +`
4 echo "$result"

In above example we will search all files in home (∼) directory up to 1 depth level, and find the ones
which contain text ls.

file read
Read contents of a file line by line.

1 #!/usr/bin/env bash
2
3 cat ~/test.txt | while read line; do
4 echo "$line"
5 done

In above example we read contents of test.txt which is in user home directory, and print it line by
line.

file write
Write to a file.
Namespaces 57

1 #!/usr/bin/env bash
2
3 lines=`docker images`
4 echo "sample header" > ~/test.txt
5 for line in ${lines}; do
6 echo "$line" >> ~/test.txt
7 done

In above example we store result of docker images command in lines variable then send sample
header text to test.txt file in home (∼) directory. Inside for loop we send each line of lines to
test.txt.
Operator > redirects output to a file and overwrite its content while operator >> will append to the
end of the file (previous contents remain there).

file write multiline


Write multiple lines into file.

1 #!/usr/bin/env bash
2
3 cat >~/test.txt <<EOL
4 Header
5
6 first line
7 second line
8 EOL

file write multiline sudo


Write multiple lines into a file which needs root permission.

1 #!/usr/bin/env bash
2
3 cat << EOL | sudo tee /test.txt
4 Header
5
6 first line
7 second line
8 EOL

remove files older than


Remove files older than x days.
Namespaces 58

1 #!/usr/bin/env bash
2
3 find ~/backup -mtime +14 | xargs rm -f

Above example removes files from ∼/backup directory which are older than two weeks.

directory
Contains directory related operations. For logical operations about directory like if a given
directory exists see logic section.

directory create
Creates a directory.

1 mkdir "test dir"

Creates test directory at the current path.

directory create nested


Create directories as required.

1 #!/usr/bin/env bash
2
3 mkdir -p "parent dir"/"child dir"

Nested Directories
Write a shell script to create a test directory containing 26 directories named from a to z
each containing 100 directories from 1 to 100 with a single command.

Directory structure should look like:


Namespaces 59

1 ── test
2 │ ├── a
3 │ │ ├── 1
4 │ │ ├── 2
5 │ . . .
6 │ . . .
7 │ │ └── 100
8 │ ├── b
9 │ │ ├── 1
10 │ │ ├── 2
11 │ . . .
12 │ . . .
13 │ │ └── 100
14 │ ├── c
15 │ . .
16 │ . .

For the answer refer to Solutions section, nested directories.

directory delete nested | directory remove nested


Delete directory and all contents. Use with caution.

1 #!/usr/bin/env bash
2
3 rm -rf /home/remisa/backups

In above example backups directory and all contents (files and directories) will be deleted from
/home/remisa path (home directory for user remisa).

directory find | file find


Find files or directories based on criteria in the given path up to N level depth.

1 #!/usr/bin/env bash
2
3 result=`find . -maxdepth 3 -type d -name "backup*"`
4 echo "$result"

Above example finds all directories (-type d) up to 3 level depth (-maxdepth 3) in the current
directory (.) where their names start with backup (-name "backup*"). To search for files use -type
f.
Namespaces 60

event
Contains event related operations available via event namespace. There are two events supported
by Shellman. EXIT and CTRL+C. Be careful about registering events multiple times. The last one you
register takes control of what happens when event fires. If you have multiple things to do, move
them all to a single function and register that function once.

event EXIT
If you need to run some commands before your script exits, you can put them in a function and call
it everywhere your script may exits. But there is an easier way to do that. Register an EXIT handler
function and it would be executed when your script execution is finished:

1 # Exit event handler


2 function on_exit() {
3 tput cnorm # Show cursor. You need this if animation is used.
4 # i.e. clean-up code here
5 exit 0 # Exit gracefully.
6 }
7
8 # Put this line at the beginning of your script (after functions used by event handl\
9 ers).
10 # Register exit event handler.
11 trap on_exit EXIT

The trap on_exit EXIT part registers on_exit function to EXIT event. You need to register events as
soon as possible in your script. But since it needs on_exit function, you need to define that function
before registering the event.
The tput cnorm part ensures we have a visible cursor when script exits. If you are using animation
feature of Shellman don’t remove it. Anyway it is harmless and you can leave it there even if no
animation is used.

event CTRL+C | event terminated


Available as CTRL+C | terminated under event namespace. If you need to do something in case your
script gets interrupted (like when user presses CTRL and C keys on keyboard) you can register a
handler function for it:
Namespaces 61

1 # CTRL+C event handler


2 function on_ctrl_c() {
3 echo # Set cursor to the next line of '^C'
4 tput cnorm # show cursor. You need this if animation is used.
5 # i.e. clean-up code here
6 exit 1 # Don't remove. Use a number (1-255) for error code.
7 }
8
9 # Put this line at the beginning of your script (after functions used by event handl\
10 ers).
11 # Register CTRL+C event handler
12 trap on_ctrl_c SIGINT

The trap on_ctrl_c SIGINT part registers on_ctrl_c function to SIGINT event. You need to register
events as soon as possible in your script. But since it needs on_ctrl_c function, you need to define
that function before registering the event.
The tput cnorm part ensures we have a visible cursor when script exits. If you are using animation
feature of Shellman don’t remove it. Anyway it is harmless and you can leave it there even if no
animation is used.

archive
Contains archive related operations like compressing and decompressing files/directories. In Linux,
tar combines files/folders into a single file without compression and mixing it with some compres-
sion utilities gives us for example archive.tar.gz. Looking at this file’s extension the tar part tells
us this file is a collection of other files/folders and gz part tells us this collection is compressed using
gzip.

archive compress tar.gz


Compress file(s)/director(ies) into a compressed archive file (.tar.gz)

1 #!/usr/bin/env bash
2
3 tar -czvf ~/archive.tar.gz ~/some-directory

In above example we are compressing and archiving a directory (some-directory) from our home
directory (denoted by ∼) into archive.tar.gz file in our home directory. This is useful for example
if we are interested to backup some-directory.

archive decompress tar.gz


Decompress an archive file (.tar.gz) into a path.
Namespaces 62

1 #!/usr/bin/env bash
2
3 tar -C ~/ -xzvf ~/archive.tar.gz

In above example we are decompressing archive.tar.gz file from our home directory into our home
directory.

archive compress tar.xz


If you need more compression than previous method, use tar.xz:
Compress file(s)/director(ies) into a compressed archive file (.tar.xz)

1 #!/usr/bin/env bash
2
3 tar -cJf ~/archive.tar.xz ~/some-directory

In above example we are compressing and archiving a directory (some-directory) from our home
directory (denoted by ∼) into archive.tar.xz file in our home directory. This file usually is smaller
than its equivalent archive.tar.gz and the compression process is slower.

archive decompress tar.xz


Decompress an archive file (.tar.xz) into a path.

1 #!/usr/bin/env bash
2
3 tar -C ~/Documents -xf ~/archive.tar.xz

In above example we are decompressing archive.tar.xz file from our home directory into
Documents directory inside our home directory.

http
Contains HTTP related operations.

http GET
Send a GET request to specified URL.
Namespaces 63

1 #!/usr/bin/env bash
2
3 curl --request GET -sL \
4 --user-agent 'Shellman' \
5 --url 'http://example.com'

Above example sends a HTTP GET request to http://example.com with desire User Agent¹².

http DELETE
Send a DELETE request to specified URL.

1 #!/usr/bin/env bash
2
3 curl --request DELETE -sL \
4 --user-agent 'Shellman' \
5 --url 'http://example.com'

http POST
Send a POST request to specified URL.

1 #!/usr/bin/env bash
2
3 curl --request POST -sL \
4 --user-agent 'Shellman' \
5 --url 'http://example.com' \
6 --data 'key1=value1' \
7 --data 'key2=value2'

http POST file


Send file with http POST.

¹²https://en.wikipedia.org/wiki/User_agent
Namespaces 64

1 #!/usr/bin/env bash
2
3 curl --request POST -sL \
4 --user-agent 'Shellman' \
5 --url 'http://example.com' \
6 --form 'key=value' \
7 --form 'file=@~/image.jpg'

Above example sends image.jpg to http://example.com via POST method.

http header
Send http request with custom header(s).

1 #!/usr/bin/env bash
2
3 curl --request GET -sL \
4 --user-agent 'Shellman' \
5 --header 'key: value' \
6 --url 'http://example.com'

http cookie
Send http request with desired cookies.

1 #!/usr/bin/env bash
2
3 curl --request GET -sL \
4 --user-agent 'Shellman' \
5 --cookie 'key=value' \
6 --url 'http://example.com'

http download
Download from url and save to desired path.
Namespaces 65

1 #!/usr/bin/env bash
2
3 curl --request GET -sL \
4 --user-agent 'Shellman' \
5 --output '~/downloaded-file.zip' \
6 --url 'http://example.com/file.zip'

ftp
Contains FTP related operations.

ftp list
Get the list of files on the ftp server at specific path.

1 #!/usr/bin/env bash
2
3 curl ftp://remisa:1234@mydomain/backup/

ftp download
Download specified file from ftp server.

1 #!/usr/bin/env bash
2
3 curl ftp://remisa:1234@mydomain/backup/latest.zip

ftp upload
Upload specified file to ftp server at desired path.

1 #!/usr/bin/env bash
2
3 curl -T test.zip ftp://remisa:1234@mydomain/backup/

ftp delete file


Delete specified file from ftp server.
Namespaces 66

1 #!/usr/bin/env bash
2
3 curl ftp://remisa:1234@mydomain/backup/test.zip -Q "DELE test.zip"

ftp rename
Rename specified file/directory on ftp server.

1 #!/usr/bin/env bash
2
3 curl ftp://remisa:1234@mydomain/backup/ -Q "-RNFR backup/test.zip" -Q "-RNTO backup/\
4 renamed.zip"

ip
Contains ip related operations.

ip local IPs
Array of local IPs.

1 #!/usr/bin/env bash
2
3 IPS=`hostname -I`
4 echo "$IPS"

ip info
public ip information (ip, city, region, country, location, postal code, organization).

1 #!/usr/bin/env bash
2
3 echo `curl -s ipinfo.io/country`
4 # U.K
Namespaces 67

ip public
Find public ip address via different services.

• bot.whatismyipaddress.com
• ident.me
• ipecho.net/plain
• icanhazip.com
• ifconfig.me
• api.ipify.org
• ipinfo.io/ip

1 #!/usr/bin/env bash
2
3 PUBLIC_IP=`curl -s api.ipify.org`
4 echo "$PUBLIC_IP"

crypto
Contains Cryptography related operations like encryption, decryption and hashing.

crypto base64 encode


Encode variable content into base64.

Base64
This encoding is used to transform binary data into string usually to save in a file or transfer
over network.

1 #!/usr/bin/env bash
2
3 base64Encoded=`echo -n "$variableToEncode" | base64`

crypto base64 decode


Decode String from base64 into Binary.
Namespaces 68

1 #!/usr/bin/env bash
2
3 base64Decoded=`echo -n "$variableToDecode" | base64 -d`

crypto hash
Hash variable content with desired algorithm.

1 #!/usr/bin/env bash
2
3 hash=`echo -n "$variableToHash" | md5sum | cut -f1 -d ' '`
4 echo "$hash"

Supported algorithms:

• md5
• sha
• sha1
• sha224
• sha256
• sha384
• sha512

process
Contains Process related information and operations.

process list
List all system processes.
Namespaces 69

1 #!/usr/bin/env bash
2
3 ps -A
4 # PID TTY TIME CMD
5 # 1 ? 00:00:03 systemd
6 # 2 ? 00:00:00 kthreadd
7 # 3 ? 00:00:01 ksoftirqd/0
8 # 5 ? 00:00:00 kworker/0:0H
9 # 7 ? 00:01:46 rcu_sched
10 # ...

process ID
Get process ID by its name. Many Linux commands need process id (PID).

1 #!/usr/bin/env bash
2
3 firefoxPID=`pgrep firefox`
4 echo $firefoxPID

process Kill
Kill a process by its name. kill command needs a PID (process ID) which we can find by pgrep
command via command substitution.

1 #!/usr/bin/env bash
2
3 sudo kill -9 `pgrep firefox`

In above example we find firefox PID and pass it to kill command. Here -9 is a switch of kill
command (kill signal). You can see a list of all signals by typing kill -l in terminal.

system
Contains System related information and operations.

system uptime
System uptime (hh:mm:ss).
Namespaces 70

1 #!/usr/bin/env bash
2
3 sys_uptime=`uptime | cut -d ' ' -f2`
4 echo "$sys_uptime" # 03:26:47

system memory info


System memory information in kilobytes (KB). Available memory information:

• MemTotal
• MemFree
• MemAvailable
• Cached
• Buffers
• Active
• Inactive
• SwapTotal
• SwapFree
• SwapCached

1 #!/usr/bin/env bash
2
3 sysMemoryMemTotal=`cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}' | head -n \
4 1`
5 echo "$sysMemoryMemTotal" # total system memory in KB

system distro name


Operating System ID (i.e. Ubuntu).

1 #!/usr/bin/env bash
2
3 distroName=`lsb_release -i | awk '{print $3}'`
4 echo "$distroName"

system distro version


Operating System release version (i.e. 16.04).
Namespaces 71

1 #!/usr/bin/env bash
2
3 distroName=`lsb_release -r | awk '{print $2}'`
4 echo "$distroName"

system distro codename


Operating System codename (i.e. xenial).

1 #!/usr/bin/env bash
2
3 distroName=`lsb_release -c | awk '{print $2}'`
4 echo "$distroName"

system kernel name


Operating System kernel name (i.e. Linux).

1 #!/usr/bin/env bash
2
3 kernelName=`uname -s`
4 echo "$kernelName" # Linux

system kernel release


Operating System kernel release (i.e. 4.4.0-140-generic).

1 #!/usr/bin/env bash
2
3 kernelRelease=`uname -r`
4 echo "$kernelRelease" # 4.4.0-140-generic

system processor type


Operating System processor type (i.e. x86_64).
Namespaces 72

1 #!/usr/bin/env bash
2
3 processorType=`uname -p`
4 echo "$processorType" # x86_64

system processor count


Number of processors (cores).

1 #!/usr/bin/env bash
2
3 processorCount=`lscpu | grep 'CPU(s)' |awk '{print $2}' | head -n 1`
4 echo "$processorCount" # 4

system processor architecture


Processor architecture (i.e. x86_64).

1 #!/usr/bin/env bash
2
3 processorArchitecture=`lscpu | grep 'Architecture' |awk '{print $2}' | head -n 1`
4 echo "$processorArchitecture" # x86_64

system processor model


Processor model name (i.e. Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz).

1 #!/usr/bin/env bash
2
3 processorModel=`lscpu | grep 'Model name' |cut -d ' ' -f 3- | sed -e 's/^[[:space:]]\
4 *//'`
5 echo "$processorModel" # Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Namespaces 73

system service manage


Manage service (daemon) operations.

• enable
• disable
• start
• stop
• reload
• restart
• status

1 sudo systemctl status network-manager

git
Contains git commands. You may need to install git on your system. Git is a version control system
for tracking changes of projects.
Install git:

• Debian-based linux systems


– sudo apt install git
• Red Hat-based linux systems
– sudo yum install git
• Archlinux
– sudo pacman -S git
• Mac
– brew install git
• Windows
– Download from https://gitforwindows.org/

git clone
Clone a repository to local machine.
Namespaces 74

1 #!/usr/bin/env bash
2
3 git clone https://github.com/user/repository.git
4 cd repository

git clone branch


Clone a repository to local machine and switch to a specific branch.

1 #!/usr/bin/env bash
2
3 git clone -b develop https://github.com/user/repository.git
4 cd repository

git config list


List git configurations.

1 #!/usr/bin/env bash
2
3 git config --list

git config set


Set a git configuration.

1 #!/usr/bin/env bash
2
3 git config --global user.name "Remisa"

git commit
Commit changes.
Namespaces 75

1 #!/usr/bin/env bash
2
3 git commit -m "fixed typo"

git commit search


Search for a commit which contains searchCriteria.

1 #!/usr/bin/env bash
2
3 git log --all --grep='typo'

git commit undo


Undo last N commits. soft preserve local changes. hard delete local changes.

1 #!/usr/bin/env bash
2
3 git reset --soft HEAD~1 # undo last local change but don't delete them

git commit list notPushed


List non pushed commits.

1 #!/usr/bin/env bash
2
3 git log origin/master..HEAD

git branch create


Create a local branch and switch into it.

1 #!/usr/bin/env bash
2
3 git checkout -b develop

git branch list


List all branches.
Namespaces 76

1 #!/usr/bin/env bash
2
3 git branch

git branch push


Push branch to remote.

1 #!/usr/bin/env bash
2
3 git push origin develop

git branch rename


Rename current branch.

1 #!/usr/bin/env bash
2
3 git branch -m newName

git branch delete local


Delete local branch.

1 #!/usr/bin/env bash
2
3 git branch --delete localBranch

git branch delete remote


Delete remote branch.

1 #!/usr/bin/env bash
2
3 git push origin --delete remoteBranch

git changes revert


Revert tracked changes.
Namespaces 77

1 #!/usr/bin/env bash
2
3 git checkout .

git patch create


Create a patch from changes.

1 #!/usr/bin/env bash
2
3 git diff > patch1.patch

git patch apply


Apply a patch from file.

1 #!/usr/bin/env bash
2
3 git apply < patch1.patch

git remote list


List all remotes.

1 #!/usr/bin/env bash
2
3 git remote

git remote urlAdd


Add remote url.

1 #!/usr/bin/env bash
2
3 git remote add origin https://github.com/user/repository.git

git remote urlChange


Change remote url.
Namespaces 78

1 #!/usr/bin/env bash
2
3 git remote set-url origin https://github.com/user/repository.git

git tag list


List all tags.

1 #!/usr/bin/env bash
2
3 git tag

git tag commit


Tag a commit.

1 #!/usr/bin/env bash
2
3 git tag -a release/1.0.0 -m "1.0.0 release"

git tag remote delete


Delete tag from remote.

1 #!/usr/bin/env bash
2
3 git push --delete origin tagName && git push origin :tagName

git tag remote push


Push tag to remote.

1 #!/usr/bin/env bash
2
3 git push origin tagName
Namespaces 79

miscellaneous
Contains other operations not available in namespaces.

switch case
This is the switch / case you may be familiar in other languages. You can define different actions
based on switch:

1 #!/usr/bin/env bash
2
3 var=2
4
5 case "$var" in
6 1)
7 echo "case 1"
8 ;;
9 2|3)
10 echo "case 2 or 3"
11 ;;
12 *)
13 echo "default action"
14 ;;
15 esac
16 # case 2 or 3

In above example we are deciding on the value of var which here is 2. If var is 2 or 3 the second case
will be triggered. If none of cases (1, 2 or 3) are triggered, * means default and that will be triggered.
change var to 5 and output will be default action.

region
Creates a region to separate different parts of script.

1 #!/usr/bin/env bash
2
3 # >>>>>>>>>>>>>>>>>>>>>>>> variables >>>>>>>>>>>>>>>>>>>>>>>>
4 var=1
5 # <<<<<<<<<<<<<<<<<<<<<<<< variables <<<<<<<<<<<<<<<<<<<<<<<<

summary
Creates a commented summary for shell script. Use it at the top of your script.
Namespaces 80

1 #!/usr/bin/env bash
2
3 # Title: title
4 # Description: description
5 # Author: author <email>
6 # Date: yyyy-mm-dd
7 # Version: 1.0.0
8
9 # Exit codes
10 # ==========
11 # 0 no error
12 # 1 script interrupted
13 # 2 error description

Document your script error codes under Exit codes section. These are code you have used in script
when it exits due to an error (i.e. exit 5 for lack of permission to do the job).

let
let is used for mathematic operations.

1 #!/usr/bin/env bash
2
3 let a=2+3
4 echo $a # 5
5 let "a = 2 + 3"
6 echo $a # 5
7 let a++ # increase a
8 echo $a # 6
9 let "a = 2 * 3"
10 echo $a # 6

assign if empty | variable default value


Assigns a value to a variable if and only if the variable is empty. Useful for assigning default values.
Namespaces 81

1 #!/usr/bin/env bash
2
3 var=""
4 : "${var:=default}"
5 echo "$var" # default
6
7 var="something"
8 : "${var:=default}"
9 echo "$var" # something

expr
It is and old command for doing arithmetic operations. Use $(( )) instead.

1 #!/usr/bin/env bash
2
3 result=`expr 2 \* 3`
4 echo "$result" # 6

Equivalent to:

1 #!/usr/bin/env bash
2
3 result=$((2 * 3))
4 echo "$result" # 6

ask question
Ask a question from user and receive its answer from input. It is possible to provide a default answer
to the question.

1 #!/usr/bin/env bash
2
3 read -ep "What is your name? " -i Remisa ANSWER
4 echo "$ANSWER" # print user's answer

timeout
Run a command within a time frame.
Namespaces 82

1 #!/usr/bin/env bash
2
3 timeout 5 curl -s http://example.com
4 echo "at most 5 seconds later"

service manager
Commands related to services. A service is a program which runs in background and doesn’t need
any user to login to be started (i.e. ssh).

1 #!/usr/bin/env bash
2
3 sudo systemctl restart service

sleep
Halt script for desired period in seconds s, minutes m, hours h, days d.

1 #!/usr/bin/env bash
2
3 sleep 2m
4 # halts script for 2 minutes

stopwatch
Use stopwatch to calculate script running time. There are three snippets related to stopwatch, use at
the given order:

1. stopwatch start: Starts stopwatch.


2. stopwatch stop: Stops stopwatch.
3. stopwatch elapsed: Calculates total time.
Namespaces 83

1 #!/usr/bin/env bash
2
3 # beginning of script
4 STOPWATCH_START_TIME=$(date +%s)
5
6 # script
7 sleep 30s
8
9
10 # end of script
11 STOPWATCH_END_TIME=$(date +%s)
12
13 # print elapsed time
14 STOPWATCH_ELAPSED_TOTAL_SECONDS=$((STOPWATCH_END_TIME - STOPWATCH_START_TIME))
15 STOPWATCH_ELAPSED_MINUTES=$((STOPWATCH_ELAPSED_TOTAL_SECONDS / 60))
16 STOPWATCH_ELAPSED_SECONDS=$((STOPWATCH_ELAPSED_TOTAL_SECONDS % 60))
17 echo elapsed $STOPWATCH_ELAPSED_MINUTES minutes and $STOPWATCH_ELAPSED_SECONDS secon\
18 ds

lib
Contains a set of library functions under fn / fx namespaces. Functions can be accessed through
fn... and their usage through fx...

Math
Math related functions.

math sum

Calculates sum of given integers. Available as fn math sum snippet.


Namespaces 84

1 #!/usr/bin/env bash
2
3 function sum () {
4 local result=0
5 for item in $@; do
6 ((result += item))
7 done
8 echo $result
9 }
10
11 var1=2; var2=5; var3=4
12 result=`sum $var1 $var2 $var3`
13 echo $result

math product

Calculates product of given integers. Available as fn math product snippet.

1 #!/usr/bin/env bash
2
3 function product () {
4 local result=1
5 for item in $@; do
6 ((result *= item))
7 done
8 echo $result
9 }
10
11 var1=2; var2=3; var3=5
12 result=`product $var1 $var2 $var3`
13 echo $result

math average

Calculates average of given integers. Available as fn math average snippet.


Namespaces 85

1 #!/usr/bin/env bash
2
3 function average () {
4 local result=0
5 for item in $@; do
6 ((result += item))
7 done
8 echo $((result / $#))
9 }
10
11 var1=2; var2=3; var3=4
12 result=`average $var1 $var2 $var3`
13 echo $result

Animation
There are three related snippets in animation namespace:

• animation frame: Use to define a frame of your animation.


• fn animation animate: A function used to animate frames.
• fx animation animate: Calling animate function.

See animation for more information.

Misc
Other useful functions.

banner simple

A function to print simple banners. To define the function use fn banner simple at the top of script
so later it can be called via fx banner simple:
Namespaces 86

1 #!/usr/bin/env bash
2
3 function banner_simple() {
4 # function body...
5 }
6
7 # call function
8 banner_simple "sample banner"

simple banner

banner color

A function to print colorful banners. To define the function use fn banner color at the top of script
so later it can be called via fx banner color:

1 #!/usr/bin/env bash
2
3 function banner_color() {
4 # function body...
5 }
6
7 # call function
8 banner_color yellow "sample banner"

color banner

import

Use functions defined in other shell script files inside your script. To define the function use fn
import at the top of script so later it can be called via fx import. Default folder for library files is
lib relative to calling script.
Namespaces 87

1 #!/usr/bin/env bash
2
3 function import() {
4 # function body...
5 }
6
7 import "mylib"
8
9 # Call some function from mylib.sh

In above example with import "mylib" we are importing functions defined in lib/mylib.sh.

options

A function to print multi choice questions. To define the function use fn options at the top of script
so later it can be called via fx options with question and choices. Default choice is zero based so 0
means first option, 1 means second…

1 #!/usr/bin/env bash
2
3 function chooseOption() {
4 # function body...
5 }
6
7 options=("one" "two" "three") # array of options
8 chooseOption "Choose:" 1 "${options[@]}"; choice=$? # call function
9 echo "${options[$choice]}" selected # print selected item by user

options 1
Namespaces 88

options 2

choice

progress

A dummy progress bar. You can use it as real progress bar with a little change.

1 #!/usr/bin/env bash
2
3 function progressBar() {
4 # function body...
5 }
6
7 progressBar .2 "Installing foo..."

progress

scan

Scan a host port range (tcp/udp).


Namespaces 89

1 #!/usr/bin/env bash
2
3 function scan () {
4 # function body...
5 }
6
7 # scan tcp ports 5000-10000 of localhost
8 scan tcp localhost 5000 10000
9
10 # tcp 8081 => open
11 # tcp 9000 => open

version compare | semver compare


There are different versioning standards out there but they are converging to a specific one called
semver¹³ over the last years. If you need to compare two different version strings which comply
with semver semantic standard, then there is a ready to use function for it as fn version compare |
fn semver compare. Insert the function into your function region:

1 #!/usr/bin/env bash
2
3 # Usage: version_compare "1.2.3" "1.1.7"
4
5 function version_compare () {
6 # function body ...
7 }

Later call the function according to usage or use fx version compare | fx semver compare snippet:

1 version_compare "major.minor.patch" "major.minor.patch"

version_compare function returns >, < or = as result. Here are some real example usage and the
outputs:

1 version_compare "1.2.3" "1.1.7" # >


2 version_compare "1.1.1" "2.1.0" # <
3 version_compare "5.0.2" "5.0.2" # =

If the two version strings are not standard semver, version_compare function will compare each part
(separated by .) until the shorter one reaches its end. If until that point both versions look like same
it returns = as result:
¹³https://semver.org
Namespaces 90

1 version_compare "3.2.2.7" "3.2.2" # =

urlencode
Encodes URL.

1 #!/usr/bin/env bash
2
3 # Usage: urlencode url
4
5 # Credit: https://unix.stackexchange.com/a/187256
6
7 function urlencode () {
8 # function body ...
9 }
10
11 # Encode the URL
12 urlencode "http://www.mysite.com/a file with spaces.html"
13
14 # Output: http%3A%2F%2Fwww.mysite.com%2Fa%20file%20with%20spaces.html

urldecode
Decodes encoded URL.

1 #!/usr/bin/env bash
2
3 # Usage: urldecode url
4
5 # Credit: https://unix.stackexchange.com/a/187256
6 function urldecode () {
7 # function body...
8 }
9
10 # Decode the URL
11 urldecode "http%3A%2F%2Fwww.mysite.com%2Fa%20file%20with%20spaces.html"
12
13 # Output: http://www.mysite.com/a file with spaces.html
Namespaces 91

animation
Why on earth one may need ASCII animation in a shell script (you can use unicode as well but it
may not work the same on all systems). To add some fun!
There are simple steps to make a beautiful animation with Shellman. You define frames and call
animate function passing it the frames array and the interval between frames in seconds. Normally
you need an animation at the end of your script, so animate function uses an infinite while loop of
course with enough sleeps.
Your frames need to have the exact same width and height. If they are different in size, fill the unused
space with spaces (no TABs).
Define animation frames at the top of your script. Here we make a simple spinner. We need frames
|, /, -, \. Use animation frame snippet:

1 IFS='' read -r -d '' frames[1] <<"EOF"


2 |
3 EOF
4
5 IFS='' read -r -d '' frames[2] <<"EOF"
6 /
7 EOF
8
9 IFS='' read -r -d '' frames[3] <<"EOF"
10 -
11 EOF
12
13 IFS='' read -r -d '' frames[4] <<"EOF"
14 \
15 EOF

Now add animate function to your script using fn animation animate snippet and call it with frames
array and desired delay between frames in seconds:
Namespaces 92

1 #!/usr/bin/env bash
2
3 IFS='' read -r -d '' frames[1] <<"EOF"
4 |
5 EOF
6
7 IFS='' read -r -d '' frames[2] <<"EOF"
8 /
9 EOF
10
11 IFS='' read -r -d '' frames[3] <<"EOF"
12 -
13 EOF
14
15 IFS='' read -r -d '' frames[4] <<"EOF"
16 \
17 EOF
18
19 function animate () {
20 local frames=("$@")
21 ((last_index=${#frames[@]} - 1))
22 local interval=${frames[last_index]}
23 unset frames[last_index]
24
25 # Comment out next two lines if you are using CTRL+C event handler.
26 trap 'tput cnorm; echo' EXIT
27 trap 'exit 127' HUP INT TERM
28
29 tput civis # hide cursor
30 tput sc # save cursor position
31
32 while true; do
33 for frame in "${frames[@]}"; do
34 tput rc # restore cursor position
35 echo "$frame"
36 sleep "$interval"
37 done
38 done
39 }
40
41 animate "${frames[@]}" 0.2

You can find base frames for your animations by searching the web for ascii art. For more examples
Namespaces 93

visit project repository page¹⁴.

pacman
Using frames is not the best way to make animations but it is simple and straightforward. I have
made another ready to use animation function for you which gets an string and eats it up.
Use fn animation pacman snippet to insert the function into your script and use fx animation
pacman to call it:

1 function pac_man () {
2 # Function body here...
3 }
4
5 pac_man "Hello World"

¹⁴https://github.com/yousefvand/shellman/tree/master/samples/animation
Solutions
Argument Parsing
Contents of greet.sh:
Argument Parsing

1 #!/usr/bin/env bash
2
3 greeting="good night"
4 name="everyone"
5
6 # >>>>>>>>>>>>>>>>>>>>>>>> Argument parsing >>>>>>>>>>>>>>>>>>>>>>>>
7
8 POSITIONAL=()
9 while [[ $# > 0 ]]; do
10 case "$1" in
11 -m|--morning)
12 greeting="good morning"
13 shift # shift once since flags have no values
14 ;;
15 -n|--name)
16 name="$2"
17 shift 2 # shift twice to bypass switch and its value
18 ;;
19 *) # unknown flag/switch
20 POSITIONAL+=("$1")
21 shift
22 ;;
23 esac
24 done
25
26 set -- "${POSITIONAL[@]}" # restore positional params
27
28 # <<<<<<<<<<<<<<<<<<<<<<<< Argument parsing <<<<<<<<<<<<<<<<<<<<<<<<
29
30 echo "$greeting $name"
Solutions 95

Nested Directories
Contents of nested-directories.sh:
Nested Directories

1 #!/usr/bin/env bash
2
3 mkdir -p test/{a..z}/{1..100}

Colorful Text
Contents of colorful-text.sh:
Nested Directories

1 #!/usr/bin/env bash
2
3 for((i=0;i<=7;i++)); do
4 echo `tput setaf $i`Hello World!`tput sgr0`
5 done

Output:

colorful text

Factorial
Contents of factorial.sh:
Solutions 96

Factorial

1 #!/usr/bin/env bash
2
3 function fact () {
4 result=1
5 for((i=2;i<=$1;i++)); do
6 result=$((result * i))
7 done
8 echo $result
9 }
10
11 # example: 4! = 4 * 3 * 2 = 24
12 fact 4

You might also like