Welcome to Bash Intro for Hackers Part 1! This material was prepared by our instructor Saeed Dehqan for our Wireless Network Attacks course as a foundational topic for anyone wanting to automate their pentesting tasks. Check out the course while you’re here! 


bash, an abbreviation of Bourne Again Shell, is the most common interactive interpreter command prompt and has a long history. First, in 1971, Unix Shell or Thompson Shell had been burned. Unix Shell was very small and had some small features. In 1975, it had been replaced by Mashey Shell, and again this version had been replaced by Bourne Shell, which we have already used.

In 1989, bash as an open-source project had been developed by Brain Fox. Brain Fox maintained and supported bash till 1994 and Chet Romey was in charge of it. Today, bash is the default shell script of Linux and MacOS10, which makes the use of bash very common.

At the time of writing this paper, the last version of bash is 5.1. Other operating systems may use other releases. For more information about this project, have a look at Chet Ramey’s website:



bash contains efficient shortcuts that you are probably familiar with. If you are using the Tilde(~) character to go to the home directory, you have used Tilde expansion.

~ character provides a home directory.

Another efficient expansion is curly brackets: {}. This kind of expansion is used to do iterative tasks. Or in other words, it is used to insert a specific range. Suppose you want to create a list of files by using touch. To do this, you can use this expansion:

As you can see, we have created four files using touch and curly bracket. Then we list files with the ls command. Of course, we can do this task without curlies but it can easily be done by that. But, still, it could be boring whenever we want to create a hundred or maybe thousands of files and it is very time-consuming. So what can we do? We can simply do that with curly brackets in a second. Look at the following example:

In this example, we have already created one hundred files that start with logfile_n. You can change the range of numbers according to your needs. Know if we want to delete files, just change the first command:

We can even adjust the range in order to delete even numbers. To do so, we add another parameter between the curlies:

The last parameter means adding two to two. Another feature we can do is the use of the alphabet instead of a number:

In this case, first I deleted previous files by asterisk expansion. Then, I created a range of files from a to z. It can be very useful to create password lists. For a terrible example, look at this one:

As you know, we have created a list of user lists that each one starts with user_{a,z}{a,z}. It can be three or four characters or whatever. This > character means to put the output of the first command to the ulist.txt file. And the echo commands print expressions. Cat command shows the content of a file.

 Other cases can be seen in the following:

Another useful work that is comprehensive and common is Arithmetic Expansion. Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is shown below:

We will discuss expressions more in the next sections.


In bash, we have an efficient tool called grep that can be used to search into the files based on regular expressions. This will help us to search behind the log files or even search in the results by using pipes. Pipes are a way to pass the output of a command to another command by using the “|” character. 

Here is a basic use of grep:

The first command is the ping with -c option, which means just send one packet. Then, we pass the output of ping to grep. The grep just returns each line that contains ‘icmp_seq’. Again, we pass the output to the cut command. Cut command will receive a line and split the line with “:” delimiter. And at the end, it will have shown the second part of the split.

In the last section, we created a ulist.txt that contains some usernames. Now, we want to show usernames that started with user_c. So, look at this example:

The -o option Only shows the matching part of the string. By default, grep just shows the lines that match with the regular expression:

As we can see, grep showed all of the usernames because ulist.txt just has one line. So, grep shows this line.

Now, we’re going to split the file. So, the above command will be executed:

Don’t worry. It’s not as sophisticated as it seems. First of all, we read the ulist.txt file. Then, we used sed to replace all (by using g switch at the end of expression) of space characters by “n” or newline. The results have been dumped to rep_ulist.txt file by > character. In the next step, we use AND(&&) expression to cat the new file. In the end, we passed rep_ulist.txt’s content to the grep to find all of the lines that match with the regexp.

In this command, we used parentheses as priorities.

We can use the -c option to show the number of results:

Another option that could be useful is -n, which shows line numbers:

You can use colors to show the results better:


bash points to the shell and you can execute your command directly in the command prompt. But our goal is to automate our scripts and for this purpose, we need to run a bunch of commands at the same time. To do this, we need to write commands in a script file and run it.

Each script file should start with a sharp and an exclamation mark and path of binary bash:


ping google.com -c 1 | grep 'icmp_seq' | cut -d ":" -f 2

Comments in bash start with #. And the postfix of bash files is sh. 

Create a file and write codes to it and save it as icmp-ttl-time.sh. To run the script, the name of the file is not enough. Thus, we need to use an interpreter. So, if we write the following command, our script will be executed:

Most of the time, we don’t want to write bash every time before the file name. Linux automatically detects the nature of the file. How? We have written in the first line a path, which bash uses to run the code:


Therefore, if we run the script without mentioning the name of the interpreter, bash will know that script must run by the /bin/bash program. Whether with the postfix of .sh or not.

But before running the script, we need to change the permission of the file:

But still, we have some limitations. If we want to run a script, we must be in the script’s directory or the path of the script needs to be specified. How can we run our script like a command? Such as grep?
To do this, first, remove postfix from the script name then copy it to the /usr/bin directory then you can run the script everywhere you want:


One of the most common commands in bash scripting is the echo command. The echo command commonly is used to print an expression to the standard output (STDOUT) but the output is changeable. The echo command is very simple. Just pass the value to it. But there are some notes about quotes that you need to know. There are three different uses for the echo command:


echo Hey $name?
echo 'Hey $name?'
echo "Hey $name?"

Now, look at the output of this script to get to know what the difference between these modes is:

The first mode is without any quotation. As you can see the variable has been detected.

In the second command, in which we used one quotation, the variable hasn’t been detected or in other words, it prints everything between quotations. The third one is like the first one.

Another feature that can be useful is to make newlines:


Like any other language, you need to use variables in your scripts. The variables should be named with a sequence of numeric and alphabetic characters, and the variable should start with the letters of the English alphabet or underline. In order to define a variable in your script, first enter the variable name, and then immediately after the equal sign, enter the value of the variable. Note that there should be no space between the equal sign and the variable name. Otherwise, it will lead to an error. It should be noted that the names of variables are case-sensitive:

To access the variables just put the $ sign before the variable:

Another way that you can use to define variables is by using let (Note that there is no need to use $ sign before the name of the variable):

You can use declare and the -r switch to define a constant:

Use the -l and -u switches to change the variable to lower or upper case, respectively:

There are a number of predefined variables in bash that are very useful.

  • The $ HOME variable causes the Linux user’s home folder to be displayed.
  • The $ PWD variable points to the current folder.
  • The $ MACHTYPE variable returns the machine type.
  • The $ HOSTNAME variable returns the hostname of the system.
  • The $ BASH-VERSION variable displays the BASH version.
  • The $ SECONDS variable displays the number of seconds that session B was running. This variable, if used within the script, returns the number of seconds the script was running.
  • The $ 0 variable returns the name of the script.

There are many predefined variables that you do not need to memorize, but being familiar with them can help you develop scripts. You can read the list of ASBASH’s built-in variables here:


Command Succession

We want to put bash commands into a script and use the output directly. Or in other words, we want to get the output of a command and write it into a variable. To do this, we need to use command succession. The command succession should be in parentheses and starts with $ character:


echo $p

In the above, the output of pwd, which is the current directory, saves in p variable prints in the output:

Without using $() the script just prints ‘pwd’ in the output. This is a simple example.

Suppose, we want to take in how much time the server response takes and we want to write a command to print the duration. For this purpose, first, we get a ping with just one packet:

ping -c 1 example.com

Then, you can pass the result of the ping to the grep, to split the result from the “bytes from” expression:

ping -c 1 example.com | grep 'bytes from' 

Next, we cut the line by using cut to get the fourth field, which contains the time of server response:

ping -c 1 example.com | grep 'bytes from' | cut -d = -f 4

Finally, we will receive the result of the above code and save it to a variable by command succession:

ping_time=$(ping -c 1 example.com | grep 'bytes from' | cut -d = -f 4)

Then, we will print it by a message:


ping_time=$(ping -c 1 example.com | grep 'bytes from' | cut -d = -f 4)
echo "The ping time is $ping_time"


Working with numbers in bash is very simple. To perform mathematical calculations in bash, you must put mathematical operations in two parentheses.

If you want the result of the calculation to be stored inside a variable, you must put a $ sign before parentheses. The values ​​in parentheses can be numbers or other variables.

bash supports six basic mathematical operators:

  • Power: $a**$b
  • Multiplication: $a*$b
  • Division: $a/$b
  • Subtraction: $a-b
  • Remainder: $a%b
  • Addition: $a+$b

In addition to the above operations, you can also use incremental($a++), decremental operators($a–), equal addition($a+=5), etc. The following example illustrates these operators:

The difference between ++n and n++ is that the first one emphasizes that first increment the variable then use it and the second one emphasizes that first use it, then increment the variable:

As you can see in the above figure, there is no need to use $ sign in these operations.

Other operations:

Keep in mind that if you do not put the operation or mathematical expression in two parentheses, the result will be the combination of two values:

Another point is that doing mathematical calculations in bash only supports numbers or integers, so, for example, performing the mathematical calculation 10/3 returns the result three because the result is a decimal number.

Comparative Operators

One of the main reasons for writing a script rather than using commands is to add comparative expressions to the commands instead of executing them directly inside the shell. For this purpose, we use two open and closed brackets.

Note that there must be a space between the brackets and expression. The output of the phrase is 0 and 1 as you guessed it, or true and false.

The results of the comparisons have been printed by echo $?. The output of the first comparison is 0, which means success, and the output of the second comparison is 1, which means failure.

But as you can see the comparisons didn’t work well. This is because the operator performs a string comparison. If you want to make a mathematical comparison, you have to do it differently. For this purpose, different operators should be used:

  • [[ $a -lt $b ]]: less than
  • [[ $a -gt $b ]]: greater than
  • [[ $a -le $b ]]: less and/or equal
  • [[ $a -ge $b ]]: greater and equal 
  • [[ $a -eq $b ]]: equal
  • [[ $a -ne $b ]]: not equal

Another way we’ve seen before is to put expressions in parentheses:

As you can see, it doesn’t work well for string comparisons. We don’t use this rule for string comparison. It is better to use this rule for integers and use the bracket rule for strings.

We can even use logical expressions like:

  • &&: and
  • ||: or
  • ^: xor

String Manipulation 

bash provides string manipulation capabilities, like joining strings together or extracting subroutines. These words may seem vague, but by looking at the examples, you get a good understanding of how they work.

Joining strings is very simple. First, we define the two variables a and b and value them. Then put them next to each other to save the result in variable c, and by printing the value of variable c, you will see the result of joining:

There can be a separator between variables:

Or even more precisely:

If you want to get the length of a string, you can add a # sign before the variable name:

You can extract certain values ​​from sub-strings. For this purpose, we add a colon character : along with a number to start the index of the character in the string. For example, below the value of variable c, we extract the substring from index 7 to the end of the string and store the result in variable d:

You can also specify the number of characters. For example, start from position 2 and extract only 4 characters after that:

Another example starts from position 2 to the first character from the last:

If we want to delete one string from another, we can use the Shortest Substring Match. Shortest Substring Match has two modes, both of which you will see below:

The first one finds the expression and removes them from the front. And the second, as you can suggest it starts from the back and removes the matches.

In the opposite of Shortest Substring Match, we have Longest Substring Match:

What is the difference between SSM and LSM? The SSM as the name implies finds the shortest substring but LSM finds the longest substring: