Introduction to Bash for Hackers. Part 2

Tr0jan_Horse

Moderator
Staff member
MODERATOR
ULTIMATE
PREMIUM
MEMBER
Joined
Oct 23, 2024
Messages
304
Reaction score
8,779
Deposit
0$
[Arrays]
In scripts, you may want to store a set of items instead of a variable. You can use arrays for this. In bash, to define arrays, we put the values in parentheses with spaces. Below, we have an empty array called a, and then we define another array called b. To retrieve a value from an array, we use a zero-based index. For example, below, by putting an index of 2, we retrieve the third value from array b:


Bash:
#!/bin/bash
a=()
b=("reason" "logic" "aspect" "ethic")
echo ${b[2]}


Now if we run the script, as you might guess, the word "aspect" will be output:

1741148208998.webp

We can also specify the array index as a number:
b[5] = "ground"
You can also use the += operator to add a value to the end of an array:
b+=("rationale")
You can use the @ index to view the entire array:


Bash:
#!/bin/bash
a=()
b=("reason" "logic" "aspect" "ethic")
b[5]="rationale"
b+=("rationale")
echo ${b[2]}
echo ${b[@]}


The result will look like this:
1741148358669.webp

[Files]
One of the main needs in bash is working with text files. The main key in this area are the < and > symbols. For example, the following command saves the specified text to a file named text.txt.
echo "something" > text.txt
With the above command, if the file exists, its contents will be replaced, and if the file does not exist, the specified contents will be inserted into it. If you want to clear the contents of a file, you can use the following command structure:
> text.txt
If you want to add a value to the end of the file and keep the previous value, use the >> symbol.
Bash:
echo "stuff" >> text.txt
So far we have talked about inserting content into a file. How do you read the contents of a file into a script?
To do this, we can use a while loop with the read command. The syntax below reads the contents of the text.txt file line by line and stores it in the text variable. We then output the contents inside the loop using the echo command.

Bash:
#!/bin/bash
while read text;
do
echo $text
done < text.txt


1741148528280.webp

Just as you can use a file as input to a loop, you can use a file as input to a command. A simple example is using the cat command to read a file:
cat < text.txt
A better example is to store the commands you want to execute in a text file, and then enter the file's contents into the command line to use it. For example, in a script you want to connect to a specific FTP server, download a file, and then exit. To do this, we'll first create a text file and store the commands we want in it.
Below we first open mirror.xmission.com with the FTP server open, connect with anonymous user and password none, upload a file and exit:
1741148637059.webp

1741148650154.webp

As you can see, the connection was successful, but the server did not allow us to upload a new file. If we want to run these commands every day, it will take a long time:

1741148674016.webp

[Condition Structures]
The if statement executes our code based on the conditions given. The structure of the if statement is such that the if phrase is inserted first, followed by a logical expression:

Bash:
if [ expr ]
if [[ expr ]]
if (( expr ))
if expr


You can use all four of the above modes. The two parentheses are used to compare integers, as we have already discussed. The use of each depends on your needs. The if structure looks like this:

Bash:
if (( 10>5 ))
then
echo "true"
else
echo "false"
fi

First, the condition in the expression is checked. If the condition is met, the block of code after the then statement is executed. Then, if the expression is not met, the else block is executed. Finally, we close the if statement with fi:

1741148784076.webp

If you need more values in an if statement, you can use elif:

Bash:
n1=10
n2=5
if (( $n1>$n2 ))
then
echo "n1 is greater than n2"
elif (( $n1<$n2 ))
then
echo "n1 is lower than n2"
else
echo "n1


First we assign the variable n1 a numeric value of 10 and the variable n2 a numeric value of 5. Then we use integers to check if the variable a is greater than 5 or not. If it is greater, a message will be printed.
The result will look like below:
1741150075665.webp

If we change the variable n1 to 4, the elif block will be executed:
1741150090074.webp

If we change the variable n1 to 5, the else block will be executed:
1741150108917.webp

[While]
A loop is used in programming to repeat an operation up to a certain number. For example, if we want to call a function a hundred times, there is no point in writing a hundred lines of code for such simplicity. There are several ways to create a loop in bash, one of which is while. An example of a simple while loop is shown below. The general structure of the work is as follows:

Bash:
#!/bin/bash
i=0
While [ $i -le 10 ]
do
echo i:$i
((i+=1))
done


Here we first create a counter variable called i with an initial value of zero. In the following lines we define the while condition such that if the value of i is less than or equal to 10, the loop will continue. And if i is greater than 10, the loop will end. Finally, we print the value of i inside the loop and then add a number to it:

1741150194206.webp

[For]
Another type of loop in bash is the for type, which creates a loop based on a set of parameters, usually one or a specific range. Using this type of loop, we get a variable that is in a specific range. A simple example can be seen below:

Bash:
#!/bin/bash
i=0
for i in 1 2 3 4 5
do
echo i:$i
done


At the beginning of the loop, we define the variable i and limit the range of numbers from 1 to 5.
Running this simple script printed the numbers one through five:
1741150275697.webp

Writing numbers one by one is not practical if we want to use a larger range of numbers. Therefore, as we explained earlier, we can use curly brackets {} to define a range of numbers or strings:
Bash:
i=0
for i in {1..5}
do
echo i:$i
done



The results will be as follows:
1741150344541.webp

Another type of for loop structure that will be familiar to you if you have worked with the C language is as follows:



Bash:
#!/bin/bash

for (( i=1; i<=10; i++ ))
do
echo $i
done


Here we first define the initial value of the variable i, then set the loop condition. Finally, we add the number to the counter i (the counter can also be: i+=2). All these elements must be enclosed in parentheses. Between each value placed there must be a semicolon ;:

1741150405103.webp

You can also use command substitution in a for loop. Consider the following example:


Bash:
#!/bin/bash

for i in $(ls)
do
echo $i
done


To do this, we add the ls command to the replace loop range.
1741151938953.webp

Thus, the results of the ls command are displayed line by line.
[Case]
What if you need to check many different elements? You can use a long list of if statements, but there is a better way. We use the term case for this purpose. The case statement checks a value based on a list of given values. Consider the following example:

Bash:
#!/bin/bash
$web="example.com"
case $web in
forest.com) echo "It's forest.";;
animal|human) echo "Maybe animal or maybe human";;
example.com) echo "This is a site!";;

*) echo $web;;
esac


First, we define the site variable and assign a value to it. Then, we use the $web variable as a base and put the test items on a separate line. In each case, we first enter the value to test, then add a closing parenthesis ) and add the required code if the test is successful. With the | symbol, we can test several things together. With the * sign, we can test other items not mentioned in the list. Note that two semicolons must be placed at the end of each line. Finally, we close the case with esac (opposite the word case). When executed, after all modes have been tested before the correct mode is tested, and at the end, the block will be executed accordingly:
1741152049907.webp

Now let's change the web variable to "terrestrial":
1741152076286.webp

[Features]
While developing a script, you may notice that many pieces of code are repeated. You can use functions to prevent duplicate blocks of code and to better organize your script. Creating functions in bash is very simple. Just type the function keyword followed by the desired function name and enter the function code in parentheses. Here is a simple example of a function in a bash script. Once a function is defined, calling it is as simple as typing the function name outside of it.


Bash:
#!/bin/bash

function greet {
echo "Hi there!"
}

greet


Above we created a greet function and called it from within a function:

1741152153629.webp

You can specify arguments to functions. For example, below we add the argument $1 to our function, and outside the function we add the argument name to the function when calling it:

Bash:
#!/bin/bash

function greet {
echo "Hi $1"
}

greet Anna


1741239555400.webp

Here we add three more arguments to our function.

Bash:
function greet {
echo "Hi $1, $2, and $3"
}

greet Anna Sara Alec


These arguments are entered in the order $1, $2, $3,... call and placed into the function.

1741239586770.webp

[Arguments]
So far we have written scripts that do not take any input. In the real world, you often need to get input from the user. To do this, we use arguments. If you read the section on functions, you will see that these arguments are exactly the same as the arguments of functions. Arguments are special variables that are set by the user when the script is executed. They can be almost anything. A file or folder that you want to create, a username, or a text string to search for.
These arguments are inserted at runtime after the user script name. The arguments are represented as numbered variables and are assigned in the order they are entered on the command line. As before, $1, $2, $3, ... you see a simple example below:



Bash:
#!/bin/bash
echo $1
echo $2
echo $3


Above we defined three arguments for the script and printed their value. When the user specifies arguments, if they contain spaces, they should be enclosed in quotation marks:

1741239663619.webp

If we want to have no limit on the number of arguments in a variable, instead of manually assigning each argument to a variable, we can create an array of arguments using $@ and a for loop. You can also count the number of assigned arguments using the $# variable:

Bash:
#!/bin/bash
for i in $@
do
echo $i
done
echo "The number of arguments: $#"


In this case, the user can enter any number of arguments into the script:
1741239716769.webp

[Flags]
If you have worked with a command line tool, you are familiar with flags. Flags are special options that you can use to pass information to a program. Flags usually start with the Dash character. You can use the getopts function to use flags in your script. Let's say we want to create a script that accepts usernames and passwords. You can see an example below.
Bash:
#!/bin/bash

while getopts u:p: option;
do
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
esac
done
echo "Username: $user"
echo "Password: $pass"


Here we use the getopts function to create a while loop with the option strings inside the case. First we enter u :p:, which specifies that our options will be -u and -p. Then inside the case statement, we place the option variable as the basis for evaluation and define the options.
$OPTARG is the value that the user sends to the script on the command line. You will see that the values we set are added:

Here we use the getopts function to create a while loop with the option strings inside the case. First we enter u :p:, which specifies that our options will be -u and -p. Then inside the case statement, we place the option variable as the basis for evaluation and define the options.

$OPTARG is the value that the user sends to the script on the command line. You will see that the values we set are added:
1741239762941.webp

The good thing about flags is that we don't have to be in any order when importing them.
[read]
We've covered how to input user data into a script via the command line. But in many cases, setting all the input values in a script is impractical. You can get user input while the script is running. If you're familiar with Python, using read is similar to Python's input function.

To do this, use the read keyword. The read keyword stops the script, gets input from the user, and stores it in the specified variable for later use. For example, below we want to get the target IP address from the user and store it in the ip variable:

Bash:

Bash:
#!/bin/bash
echo "Target IP Address"
read ip
echo "Target IP Address is: $ip"


Here you will get the IP and just write it:
1741239838498.webp

There are other features. Consider the following example:

Bash:
#!/bin/bash
echo "Please enter a username"
read user
echo "Please enter a password"
read -s pass
read -p "The IP Address?" ip
echo username: $user password: $pass IP Address: $ip


The -s option prevents user input from being displayed. This option is typically used when entering a password, as we don't want it to be displayed on the console. You can put everything on one line by adding the -p option:
1741239880268.webp

When executing the script, the necessary elements will be received from the user, and you can then use them in your script to perform operations.

[select]
Another way to get input from the user is to use the select keyword, which works like a menu. To do this, first type the select keyword, then the name of your variable, then the word in, then a list of options to choose from. Then type the do keyword, and then the contents of the executable file. Here, we only output the value that the user selected using the echo command. It is very important to end the loop with the break keyword:


Bash:
#!/bin/bash

select ogy in "Psychology" "Psychiatry" "Criminology" "Astrology"
do
echo "Your selected major is $ogy"
break
done


When the above script is executed, the user is shown a list of selected items. Once the user is able to enter the desired number, he will be able to determine the selected route.

1741239939070.webp

This function can also be written using a case structure. Enter a case statement here and create a different output for each case. Note that we have added an Exit option that, if the user enters *, will execute a break and exit the program. The * option also calls other items that are not listed:



Bash:
select ogy in "Psychology" "Psychiatry" "Criminology" "Astrology"
do
case $option in
"Psychology")  echo "Psychology is the science of mind and behavior.";;
"Psychiatry")  echo "Psychiatry is the medical specialty devoted to the diagnosis, prevention, and treatment of mental disorders.";;
"Criminology") echo "Criminology is the study of crime and deviant behaviour.";;
"Astrology")   echo "Astrology is a pseudoscience that claims to divine information about human affairs and terrestrial events by studying the movements and relative positions of celestial objects";;
"exit")    break;;
*)     echo "Not defined"
esac
done


If we run the script, we first enter option 2 to display the output of Psychiatry, and then we enter another option. Finally, we exit the program:

1741239971522.webp

[Exception Management]
While it is important to get input from the user, you also need to manage possible errors. What happens if the user doesn't enter any parameters and presses Enter? Does your script work correctly in this situation? First, let's look at an example where the script requires three arguments to run:


Bash:
#!/bin/bash

if [ $# -lt 3 ];
then
cat <<- END
This script needs three arguments:
./exception <TARGET> <PORT> <SCHEME>
END
else
echo "$3://$1:$2"
fi


This script first checks the number of arguments using $# and if the number of arguments entered is less than three, the here-doc outputs the text as a hint to the user. If the number of arguments is correct, the data entered in the script will be output:

1741240012773.webp

First we enter without arguments. We get a warning. When we enter one argument, we get a warning again, but if we enter three arguments, the script will execute correctly.

Another way is to create a loop that requires the user to only enter a value, otherwise they will get an error message:
Bash:
#!/bin/bash

read -p "hostname: " host
while [[ -z "$host" ]]
do
read -p "hostname has not been entered!
hostname: " host
done
echo "Entered hostname is $host"


This notification continues until the user enters the required amount:

1741240042736.webp

The best way is to give the user the option to choose a default answer:


Bash:


Bash:
#!/bin/bash
read -p "hostname: [default:localhost] " host
while [[ -z "$host" ]]
do
host="localhost"
done
echo "entered hostname is $host"


If the user presses enter, localhost will be used by default. The -z option means that the host variable has no value:
1741240076166.webp

Another method of data validation is regular expressions:



Bash:
#!/bin/bash
read -p "hostname: " host
while [[ ! $host =~ [\w\.]+ ]]
do
read -p "The hostname is incorrect:  $host
hostname: " host
done
echo "Entered hostname is $host"


If the entered data does not match the specified regular expressions, we will receive a warning about the need to enter the correct data:
1741240104567.webp
 
Top Bottom