# Exploring Bash Scripting
### checking the specific bash version
```bash
bash --version
```
### checking the environment variables
*bash Varibles https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html*
```bash
env
env ${ENVIRONMENT_VARIABLES}
```
### listing processes
```bash
ps -e -f
```
*ps stands for list processes*
*-e stands for every process*
*-f stands for full listing*
### checking for available space
```bash
df --human-readable
df -h
```
## Elements of a Bash Script
*Using google standard Guide https://google.github.io/styleguide/shellguide.html*
### The shebang line
```bash
#!/bin/bash
```
### Optional Arguments and what they do
```bash
#!/bin/bash -x
```
*Prints all the commands and arguments as they are present in the script, very useful for debugging*
```bash
#!/bin/bash -r
```
*Creates a restricted bash shell*
```bash
bash -r myScript.sh
```
*Passing an argument to the bash interpreter*
### Writing comments in bash
```bash
#!/bin/bash
# This is my first script
```
### A simple script that prints Hello World!
```bash
#!/bin/bash
echo "Hello World!"
```
### Debugging scripts
```bash
bash -n myscript.sh
```
*This performs a dry run without executing any codes but highlights and print syntax errors on the code*
```bash
bash -x myscript.sh
```
*executes the codes in realtime using verbose mode and highlights the error while running and helps to debug*
```bash
#!/bin/bash
set -x
--snip--
set +x
```
*This performs point to point debugging, where set is like an on and off switch*
### A script that creates a directory and files within it and list the files
```bash
#!/bin/bash
# Create the Directory
mkdir myDirectory
# Create two files in the Directory
touch myDirectory/file1
touch myDirectory/file2
ls -l myDirectory
echo "......end of script"
```
### Variables in Bash
*The following rules govern the naming of bash variables:
• They can include alphanumeric characters.
• They cannot start with a number.
• They can contain an underscore (_).
• They cannot contain whitespace*
*In bash, variables are considered type strings by default.
### Assigning and Accessing Variables
```bash
# assigning the variable
book="Black hat bash"
# reading the value of the variable using ${variable_name}
echo "The name of the book is ${book}"
#reading the value of the variable using $Variable_name
echo "The name of the book is $book"
```
### Assigning output of a command to a variable
```bash
root_directory=$(ls -ld /)
echo "${root_directory}"
```
### Unassigning Variables
*Unassigning variables is done using the unset command*
```bash
unset book
# This removes the value set in the vook variable initially
```
### Scoping Variables
#### Global Variables are those variables whose values available to the entire program.
#### Local variables are those whose values are available within the local scope.
```bash
#!/bin/bash
PUBLISHER="No Starch Press"
print_name(){
local name
name="Black Hat Bash"
echo "${name} by ${PUBLISHER}"
}
print_name
echo "Variable ${name} will not be printed because it is a local variable, but variable ${PUBLISHER} will be printed because it is a global variable."
```
```bash
$ ./printName.sh
Black Hat Bash by No Starch Press
Variable will not be printed because it is a local variable, but variable No Starch Press will be printed because it is a global variable.
```
### Arithmetic Operators
*full details on operators are available here https://tldp.org/LDP/abs/html/ops.html*
<pre>
Operator Description
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulo
+= Incrementing by a constant
-= Decrementing by a constant
</pre>
*you can perform arithmetic operation using different methods*
### using the let Command
```bash=
let result="5 * 10"
echo "$result" # prints 50
```
### using the double parenthesis command
```bash
result=$((5 * 10))
echo "${result}" # prints 5
```
### using the expr command
```bash
complete=$(expr 20 + 40)
echo "${complete}" # prints 60
```
### more thins to do with expr
```bash=
#!/bin/bash
# Get the length of a specific string
expr length "string" # prints 5
# Get the substring of a string with a specific length
# expr substr "string" from <start lenght> to <length after start>
expr substr "My test string" 4 4 # prints test
# In modern scripting, you can also do this
string="my test string"
# echo ${string:position:length}
echo ${string:3:4} #prints test
# Match a specific substring against an anchored pattern
*syntax*
# expr match <string/variable> <pattern>
# Match using regular expression
expr match "abc123xyz" '[a-z]*' # prints 3 (matching the a,b and c in the string)
# Match using a substring
expr match "hello world" 'hello' # prints 5
expr match "hello world" : 'hello' # prints 5
# Get the first character position from a specific set in a string
*syntax*
# expr index "string" "char"
expr index "my name is StingRay Okafor" "o" # prints 25 as thats where the character small o was found.
# find the position of any character in a set
expr index "hello world" "aeiou" # prints 2, as e was first found in the second position of hello
```
### Arrays in bash
```bash
#!/bin/bash
# Sets an array
IP_ADDRESSES=(192.168.1.1 192.168.1.2 192.168.1.3)
# Prints all elements in the array
echo "Print all IP Addresses in the list"
echo "${IP_ADDRESSES[*]}"
# Prints only the first element in the array
echo "Print only the first IP Address in the list"
echo "${IP_ADDRESSES[0]}"
```
### Deleting elements from an Array using unset
```bash
unset IP_ADDRESSES[1]
```
### Swapping array values with some other value
```bash
IP_ADDRESSES[1]=192.168.1.10
```
### Streams
*Streams are files that act as a communication channel between a program and its environment. Bash has 3 standard streams*
<table>
<thead>
<tr>
<th>Stream Name</th>
<th>Description</th>
<th>File Descriptor Number</th>
</tr>
</thead>
<tbody>
<tr>
<td>Standard Input (stdin) </td>
<td> Data coming into a program as input</td>
<td>0</td>
</tr>
<tr>
<td>Standard Output (stdout) </td>
<td> Data coming out of a program as an output</td>
<td>1</td>
</tr>
<tr>
<td>Standard Error (stderr)</td>
<td>Errors coming out of a program</td>
<td>2</td>
</tr>
</tbody>
</table>
### Control Operators in Bash
<em>These are tokens that perform operations in bash</em>
<table>
<thead>
<tr>
<th>Operator</th>
<th>Descriptions</th>
</tr>
</thead>
<tbody>
<tr>
<td>&</td>
<td>Sends a command to the background</td>
</tr>
<tr>
<td>&&</td>
<td>Logical AND operator</td>
</tr>
<tr>
<td>( )</td>
<td>Used to group commands together</td>
</tr>
<tr>
<td>;</td>
<td>Used as a list terminator A command following the terminator will run
after the preceding command has finished, regardless of whether it
evaluates to true or not</td>
</tr>
<tr>
<td>;;</td>
<td>ends a case statement</td>
</tr>
<tr>
<td>|</td>
<td>Redirects the output of a command as an input of another command</td>
</tr>
<tr>
<td>||</td>
<td>Logical OR Operator</td>
</tr>
</tbody>
</table>
### Logical AND Operator
```bash
sudo apt update && sudo apt upgrade -y
```
*The second command only executes if the first command is successful*
### Parenthesis Operator
```bash
(ps; df --human-readable)
```
*This runs the processes command, then move to run the df command which prints the available disc spaces in human readable format*
### Semi-colon operator ;
```bash
ls;ps;whoami
```
*This lists available files and directories in the current directory, then prints the processes running and move on to print the current user*
```bash
# print the current working directory, list the contents, run the process command and print the current user
pwd;ls;ps;whoami
```
### Chaining commands with the OR operator ||
```bash
pwd || echo "the pwd command failed"
```
*The command above prints the working directory and ignores the other command; hence, once one of each side works, the other is ignored*
## Redirecting Operators
*The three standard streams {stdin, stdout, stderr}, highlighted earlier are redirected from one program to another.
<table>
<thead>
<tr>
<th>Operator</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>></td>
<td>Redirects stdout to a file</td>
</tr>
<tr>
<td>>></td>
<td>Redirects stdout to a file by appending it to an existing content</td>
</tr>
<tr>
<td>>& or &></td>
<td>Redirects stdout and stderr to a file</td>
</tr>
<tr>
<td>&>></td>
<td>Redirects stdout and stderr to a file by appending it to an existing file</td>
</tr>
<tr>
<td> < </td>
<td>Redirects input to a command</td>
</tr>
<tr>
<td> << </td>
<td>Called a here document, or heredoc, redirects multiple input lines
to a command</td>
</tr>
<tr>
<td> | </td>
<td>Redirects output of a command as input to another command (Pipe)</td>
</tr>
</tbody>
</table>
### Redirection Operators Application
* > operator*
```bash
echo "This is my first redirection" > output.txt
# The output.txt file will contain the string above
```
* >> operator*
```bash
echo "This s a second line in my redirections" >> output.txt
# This prints the above string in a new line below the previous string in the output.txt file
```
* &> operator*
```bash
# standard output and standard error redirection
ls -l / &> stdout_and_stderr.txt
cat stdout_and_stderr.txt
## to append outputs to the stdout_and_stderr.txt file we use the ampersand and double chevron
ls -l / &>> stdout_and_stderr.txt
```
* 1> and 2> operators*
```bash
# Sending the standard output to one file and the standard error ot another file we do this
ls -l / 1> stdout.txt 2> stderr.txt
# using the error 2> command to log errors and send them to a file during runtime
lzl 2> error.txt
# lzl is not a real linux command hence it will throw an error, which is sent to the error.txt file.
$ cat error.txt # displays the error from the lzl command
```
### standard input stream using the < operator
```bash
cat < output.txt
# note that the file could be in any accurate format.
# this is mostly used to for a single file
```
### standard input stream using the << operator
```bash
# mostly used to redirect multiple lines to a command
#!/bin/bash
cat << EOF
This is my first rodeo
However, I am still thinking of my second
And maybe third
EOF
# save this to a file named test_mul_red.sh
# make it executable
chmod u+x test_mul_red.sh
# run the script
./test_mul_red.sh
```
### Pipe redirect operator |
```bash
ls -l / | grep "bin"
# this command lists in the long format the root directory and searches for the folder with "bin" as part of the name and outputs them.
```
## Positional Arguments
```bash
#!/bin/bash
# This script will png any address provided as an argument
# ${0} is assigned by default to the script name
# ${1} takes the first positional argument, which is either a domain name, or an IP Address
SCRIPT_NAME="${0}"
TARGET="${1}"
echo "Running the script ${SCRIPT_NAME}..."
echo "Pinging the target: ${TARGET}..."
ping "${TARGET}"
```
*Save the script as ping_with_arg.sh
```bash
chmod u+x ping_with_arg.sh
```
```bash
# Run the scrip with
./ping_with_script.sh nostarch.com
```
<pre>
The next script takes an input parameter from the user and pings the domain 10 times and return the result
</pre>
```bash
#!/bin/bash
# This script tells you what to do
echo "After seeing the script name below, you will be asked to input an argument"
# Assigning the first parameter to the script.
SCRIPT_NAME="${0}"
# Running this should print the script name
echo "The script name is $SCRIPT_NAME"
sleep 2
# Ask the user to input an argument
read -p "Enter the target domain name you intend to ping: " TARGET
# Check if an input was provided by the user
if [[ -z "${TARGET}" ]]; then
echo "No target domain proided. Exiting ....."
exit 1
fi
# Display a message and wait
echo "Pinging ${TARGET}..."
sleep 2
# Execute the ping command
ping -c 10 "${TARGET}"
# Exit the script
echo "Ping completed. Exiting..."
exit 0
```
### Detailed pinging commands
<pre>- <font color="#5EBDAB">Ping host:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping </font>host
- <font color="#5EBDAB">Ping a host only a specific number of times:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping -c </font>count<font color="#FEA44C"> </font>host
- <font color="#5EBDAB">Ping host, specifying the interval in seconds between requests (default is 1 se</font>
<font color="#5EBDAB">cond):</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping -i </font>seconds<font color="#FEA44C"> </font>host
- <font color="#5EBDAB">Ping host without trying to lookup symbolic names for addresses:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping -n </font>host
- <font color="#5EBDAB">Ping host and ring the bell when a packet is received (if your terminal support</font>
<font color="#5EBDAB">s it):</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping -a </font>host
- <font color="#5EBDAB">Also display a message if no response was received:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping -O </font>host
- <font color="#5EBDAB">Ping a host with specific number of pings, timeout (-W) for each reply, and tot</font>
<font color="#5EBDAB">al time limit (-w) of the entire ping run:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">ping -c </font>count<font color="#FEA44C"> -W </font>seconds<font color="#FEA44C"> -w </font>seconds<font color="#FEA44C"> </font>host
</pre>
### Accessing all arguments in a script is done with $@
```bash
#!/bin/bash
# This shows all the arguments passed
echo "The arguments are: $@"
```
### Accessing the total number of arguments in a script is done with $#
```bash
#!/bin/bash
# This tells us the total number of arguments passed
echo "The total number of arguments is: $#"
```
### Variables related to positional arguments
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>$0</td>
<td>The name of the script file</td>
</tr>
<tr>
<td>$1, $2, $3</td>
<td>Positional arguments</td>
</tr>
<tr>
<td>$#</td>
<td>Number of passed positional arguments</td>
</tr>
<tr>
<td>$*</td>
<td>All positional arguments</td>
</tr>
<tr>
<td>$@</td>
<td>All positional arguments, where each argument is individually quoted</td>
</tr>
</tbody>
</table>
### The difference between $@ and $* in a loop
<pre>
Create a script named test_args.sh
copy and paste the script below
Chmod u+x test_args.sh
# run the command
bash test_args.sh "1" "2" "3" "4" "5"
Observe how $@ displays on each new line once the script is run, while $* displays on one line
</pre>
```bash
#!/bin/bash
# Change "$@" to "$*" to observe the behaviour
echo "Below shows the \$@ display"
for args in "$@"; do
echo "${args}"
done
echo "Below shows the \$* display"
for args in "$*"; do
echo "${args}"
done
```
*Output*
<pre>
Below shows the $@ display
1
2
3
4
5
Below shows the $* display
1 2 3 4 5
</pre>
### Input prompting
<pre>
A script that asks users for inputing their first name and last name, stores it as a variable using "read -r", and prints an output
</pre>
```bash
#!/bin/bash
# Takes input from the user with "read -r", and assigns it to a variable
echo "What is your first name?"
read -r firstname
echo "What is your last name?"
read -r lastname
echo "Your first name is ${firstname} and your last name is ${lastname}"
```
### Everything you can do with the read command in bash
<pre>- <font color="#5EBDAB">Store data that you type from the keyboard:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read </font>variable
- <font color="#5EBDAB">Store each of the next lines you enter as values of an array:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read -a </font>array
- <font color="#5EBDAB">Specify the number of maximum characters to be read:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read -n </font>character_count<font color="#FEA44C"> </font>variable
- <font color="#5EBDAB">Assign multiple values to multiple variables:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read </font>_ variable1 _ variable2<font color="#FEA44C"> <<< "</font>The surname is Bond<font color="#FEA44C">"</font>
- <font color="#5EBDAB">Do not let backslash (\) act as an escape character:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read -r </font>variable
- <font color="#5EBDAB">Display a prompt before the input:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read -p "</font>Enter your input here: <font color="#FEA44C">" </font>variable
- <font color="#5EBDAB">Do not echo typed characters (silent mode):</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">read -s </font>variable
- <font color="#5EBDAB">Read stdin and perform an action on every line:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">while read line; do </font>echo|ls|rm|...<font color="#FEA44C"> "$line"; done < </font>/dev/stdin|path/to/file|...
</pre>
### Working with dates in Bash
<pre> - <font color="#5EBDAB">Display the current date using the default locale's format:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date +%c</font>
- <font color="#5EBDAB">Display the current date in UTC, using the ISO 8601 format:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date -u +%Y-%m-%dT%H:%M:%S%Z</font>
- <font color="#5EBDAB">Display the current date as a Unix timestamp (seconds since the Unix epoch):</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date +%s</font>
- <font color="#5EBDAB">Convert a date specified as a Unix timestamp to the default format:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date -d @</font>1473305798
- <font color="#5EBDAB">Convert a given date to the Unix timestamp format:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date -d "</font>2018-09-01 00:00<font color="#FEA44C">" +%s --utc</font>
- <font color="#5EBDAB">Display the current date using the RFC-3339 format (YYYY-MM-DD hh:mm:ss TZ):</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date --rfc-3339 s</font>
- <font color="#5EBDAB">Set the current date using the format MMDDhhmmYYYY.ss (YYYY and .ss are optiona</font>
<font color="#5EBDAB">l):</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date </font>093023592021.59
- <font color="#5EBDAB">Display the current ISO week number:</font>
<font color="#5EBDAB"> </font><font color="#FEA44C">date +%V</font>
</pre>