DEV Community

Cover image for πŸš€ The Missing Shell Scripting Crash Course
Sahil Bondre
Sahil Bondre

Posted on • Updated on

πŸš€ The Missing Shell Scripting Crash Course

Bash is a shell and a command language. You can use it like any other language to write scripts. Bash scripts can run on Linux and Mac right off. For Windows, there's a small workout to be done. In this tutorial I'll be going over Shell Syntax and not Shell Commands like ls, grep, cat.

This crash-course will teach you all the necessary stuff you need to get up and running with writing shell scripts.

1. Hello World

All you need in this tutorial is your terminal and a text editor. Let's write a simple hello world in bash. Create a new file called script.sh and write the following code:

#!/bin/bash

echo "Hello World"
Enter fullscreen mode Exit fullscreen mode

Now save this file. To make this executable, run chmod +x ./script.sh in your terminal. Then you can run the above script using ./script.sh in your terminal.

Hello World
Enter fullscreen mode Exit fullscreen mode

Tah Dah! Your first bash script. Let's examine the code closely. The first line #!/bin/bash is called a shebang. It tells your computer which program to give this code to run. In our case, it is /bin/bash.
If you wanted to write javascript instead of bash you could write the following code.

#!/bin/node

console.log("Hello Javascript")'
Enter fullscreen mode Exit fullscreen mode
$ ./script.sh
Hello Javascript
Enter fullscreen mode Exit fullscreen mode

The above code will work only if you have node installed in /bin/ directory. So essentially you can write code in any language. All you need to do is specify which program can handle that code and your system will pass on that file to that program.

Let's hop back to bash now. In the above code, the second line is echo "Hello World". echo is a command which prints stuff to the Standard Output which is the terminal in our case.

2. Variables

Let's modify the above code a little bit.

#!/bin/bash

name="Bash"

echo "Hello, $name"
Enter fullscreen mode Exit fullscreen mode
$ ./script.sh
Hello, Bash
Enter fullscreen mode Exit fullscreen mode

We create a variable called name and store the string "Bash" to it. Then to access it, we need to reference it with $. If you forget the $ sign, bash will treat name as a string literal, and you'll get Hello name as output instead.

Note that there are no spaces around the = while defining a variable. Also, use double quotes while referencing.

3. User Input

Let's continue modifying our script. We'll request the user for their name and then greet them back.

#!/bin/bash

read -p "What is you name: " name

echo "Hello $name"
Enter fullscreen mode Exit fullscreen mode
$ ./script.sh
What is you name: godcrampy
Hello godcrampy
Enter fullscreen mode Exit fullscreen mode

The read command takes in one line of input from the Standard Input and saves it to the variable name. The -p flag allows us to pass a prompt of "What is your name: " before taking in the input.

One neat trick to reference variables in a string is to use curly braces:

#!/bin/bash

read -p "Enter an action: " verb

echo "You are ${verb}ing"
Enter fullscreen mode Exit fullscreen mode
$ ./script.sh
Enter an action: cook
You are cooking
Enter fullscreen mode Exit fullscreen mode

4. Comments

# Comments start with hash
Enter fullscreen mode Exit fullscreen mode

Multiline Comments are a bit of a complicated mess.

5. Arguments

$1, $2 and so on, store the arguments passed into the script. $0 stores the file name.

#!/bin/bash

echo $0
echo $1
echo $2
echo "${@}" # Access all the arguments [More on this later]
Enter fullscreen mode Exit fullscreen mode
$ ./script.sh first second
./script.sh
first
second
first second
Enter fullscreen mode Exit fullscreen mode

6. Expressions

There are 3 common expressions: return values, arithmetic evaluation and test command. All three of these return either a true or false value.

Return Values
This is literally the return value of programs like grep, find.

Arithmetic Evaluation
Bash uses a parenthesis to denote arithmetic expressions.
Example: (( 1 <= 5))

Test Command
The test command often denoted by [ or [[ can carry out more complex operations:

[[ -e "$file" ]] # True if file exists
[[ -d "$file" ]] # True if file exists and is a directory
[[ -f "$file" ]] # True if file exists and is a regular file
[[ -z "$str" ]]  # True if string is of length zero
[[ -n "$str" ]]  # True is string is not of length zero

# Compare Strings
[[ "$str1" == "$str2" ]]
[[ "$str1" != "$str2" ]]

# Integer Comparisions
[[ "$int1" -eq "$int2" ]] # $int1 == $int2
[[ "$int1" -ne "$int2" ]] # $int1 != $int2
[[ "$int1" -gt "$int2" ]] # $int1 > $int2
[[ "$int1" -lt "$int2" ]] # $int1 < $int2
[[ "$int1" -ge "$int2" ]] # $int1 >= $int2
[[ "$int1" -le "$int2" ]] # $int1 <= $int2
Enter fullscreen mode Exit fullscreen mode

Note that [ is actually a command. Try running $ where [.
test, [ and [[ are almost similar. There are a few subtle differences.

And & Or

[[ ... ]] && [[ ... ]] # And
[[ ... ]] || [[ ... ]] # Or
Enter fullscreen mode Exit fullscreen mode

7. Conditionals

Now since we know, expressions let's use them in conditional statements.

# 1. Return values

# If notes.md file doesn't exist, create one and 
# add the text "created by bash"
if find notes.md
then
  echo "notes.md file already exists"
else
  echo "created by bash" | cat >> notes.md
fi
Enter fullscreen mode Exit fullscreen mode
# 2. Arithmetic Evaluations
read -p "Enter your age: " age

if (( "$age" > 18 ))
then
  echo "Adult!"
elif (( "$age" > 12 ))
then
  echo "Teen!"
else
  echo "Kid"
fi
Enter fullscreen mode Exit fullscreen mode
# 3. Test Expressions
# Check if argument was passed
# "$1" corresponds to first argument
if [[ -n "$1" ]]
then
  echo "Your first argument was $1"
else
  echo "No Arguments passed"
fi
Enter fullscreen mode Exit fullscreen mode

The if statement has to end with fi.

If you are keen enough, you might wonder why I am using "$var" and not $var to reference the variables. Here's your answer. (Hint: Both work but double quotes are safer).

8. Looping

# print numbers 1 to 10

# c like for loop
for (( i = 1; i <= 10; ++i ))
do
  echo "$i"
done

# for in
for i in {1..10}
do
  echo "$i"
done

# while
i=1
while [[ "$i" -le 10 ]]
do
  echo "$i"
  ((i++))
done

# until
i=1
until [[ "$i" -eq 11 ]]
do
  echo "unitl $i"
  ((i++))
done
Enter fullscreen mode Exit fullscreen mode

Iterating over arrays
Arrays are declared using parenthesis without commas between elements. More on arrays later on.

arr=(a b c d)

# For in
for i in "${arr[@]}"
do
  echo "$i"
done

# c like for
for (( i = 0; i < "${#arr[@]}"; i++))
do
  echo "${arr[$i]}"
done

# while
i=0
while [[ "$i" -le "${#arr[@]}" ]]
do
  echo "${arr[$i]}"
  (( i++ ))
done
Enter fullscreen mode Exit fullscreen mode

${arr[@]} allows you to iterate over an array while ${#arr[@]} returns the length of the array.

Iterating over arguments
@ holds all the arguments passed in to the script

for i in "$@"
do
  echo "$i"
done
Enter fullscreen mode Exit fullscreen mode

continue and break work the same way as in other programming languages. continue skips the current iteration. break quits the loop.

9. Arrays

Arrays in bash are defined with parenthesis with no commas separating the elements.

arr=(a b c d)
Enter fullscreen mode Exit fullscreen mode

Read

echo "${arr[1]}"     # Single element
echo "${arr[-1]}"    # Last element
echo "${arr[@]:1}"   # Elements from 1
echo "${arr[@]:1:3}" # Elements from 1 to 3
Enter fullscreen mode Exit fullscreen mode

Insert

arr[5]=e                            # direct address and insert/update
arr=(${arr[@]:0:1} new ${arr[@]:1}) # Adding 'new' to array
Enter fullscreen mode Exit fullscreen mode

Delete

arr=(a b c d)
unset arr[1]
echo << "${arr[1]}" # Outputs nothing
Enter fullscreen mode Exit fullscreen mode

Notice how once we delete the element at position 1, the following item does not take up its place. Once removing is done, we need to re-index the array.

arr=(a b c d)
unset arr[1]
arr=("${arr[@]}")
echo << "${arr[1]}" # c
Enter fullscreen mode Exit fullscreen mode

10. Functions

Functions in bash have a kind of similar syntax as in other programming languages. Arguments to a function are accessed identically to the arguments to the script.

greet() {
  echo "Hello, $1"
}

greet Bash # Hello, Bash
Enter fullscreen mode Exit fullscreen mode

The function definition does not specify any information of the arguments passed to it. Notice how while calling a function, parenthesis is not used. Instead, arguments are passed in separated by space.

All the arguments of a function can be accessed using @.

greet() {
  echo "Hello, ${@}"
}

greet every single body # Hello, every single body
Enter fullscreen mode Exit fullscreen mode

And that's it for this crash course. You can now start writing your own shell scripts. There's more to be explored beyond what I have mentioned here.

🌟 I made some Cheat-Sheets
πŸš€ Find me on Instagram | Github | Twitter | Website
πŸ˜„ Have a wonderful day!

Oldest comments (19)

Collapse
 
irregula_exprsn profile image
Muhammad Iliyas

πŸ‘πŸ½πŸ‘πŸ½πŸ‘πŸ½

Collapse
 
dsbarnes profile image
dsbarnes

This is an excellent article. I've been working (less than I'd like to admit) on learning Bash. I've observed that floating point arithmetic is amazingly difficult (Or i've missed something obvious) and dealing with dates that are not GNU formatted typically yields my writing embarrassing and nonsensical pipes into sed / awk.

Any pointers for doing floating point math and handling dates for newbs like me?

Appreciate your sharing.

Collapse
 
godcrampy profile image
Sahil Bondre

Let me tell you a secret. Before writing this article I didn't know Bash myself. I've realized that teaching is the best way to learn. Maybe you can write a part two to this article explaining dates and floating-pointing arithmetic. I'm pretty sure you'll learn a lot from the process! Hope this helps πŸ˜ƒ

Collapse
 
pystar profile image
Pystar

Hello @dsbarnes ,
You can not do floating point arithmetic natively in Bash, but there is a trick I use i.e. using the 'bc' command like:

echo "3.142 + 3.142" | bc -l # add the value of pi to itself
echo "sqrt(49)" | bc -l # find the square root of 49
echo "scale=2; sqrt(91)" | bc -l # To find the square root of 91 to just 2 decimal places.

The capabilities of bc is extremely wide. Check the Manpage for its full documentation.

Collapse
 
hoss3inf profile image
Hossein Farrokhi

on the begining of the tutorial, i had to use chmod +x ./script.sh instead of chmod -x ./script.sh to make it executable, am I missing something?

Collapse
 
godcrampy profile image
Sahil Bondre

Yup, yours is correct. I added the - out of habit! Fixed. Thanks πŸ˜„

Collapse
 
tslarge profile image
Travis Large • Edited

A well written and concise crash course! This is a great diving board into other shell scripting topics like simple automation.

Collapse
 
godcrampy profile image
Sahil Bondre

Thanks πŸ˜„

Collapse
 
thechazhall profile image
C.F. Hall

Thanks great article.

Collapse
 
godcrampy profile image
Sahil Bondre

I'm glad! And thank-you for reading!

Collapse
 
shlomilachmish profile image
shlomi-lachmish

Thanks for this fun handzone πŸ‘Œ

**small typo in section 6
[[ -n "$str" ]] # True if string is not of length zero

Collapse
 
godcrampy profile image
Sahil Bondre

fixed!

Collapse
 
arnebab profile image
Arne Babenhauserheide • Edited

Thank you for your tutorial!

There’s one thing I see missing: If you use bash, you can do quick string operations in variables:

A=abc123foo.txt

strip suffix:
echo ${A%.txt} # abc123foo
strip suffix with globbing:
echo ${A%foo*} # abc123

strip prefix:
echo ${A#abc} # 123foo.txt
strip prefix with globbing:
echo ${A#*c} # 123foo.txt

Collapse
 
godcrampy profile image
Sahil Bondre

Thanks, that's really useful!

Collapse
 
crcastle profile image
Chris Castle

This is a great article! Once you get the basics down, ShellCheck is a great tool to use to make these and other syntax and style "rules" easier to remember (or not have to remember at all!). shellcheck.net

It can be easily added as a linter to Vim, Emacs, VS Code, and others.

ShellCheck screenshot

Collapse
 
godcrampy profile image
Sahil Bondre

This sounds like something I definitely need! Thanks!

Collapse
 
totallymustafa profile image
Syed Ahmad Mustafa

This is really useful, you saved me a lot of time. Also, great posts πŸ‘ŒπŸ»πŸ‘ŒπŸ».

Collapse
 
godcrampy profile image
Sahil Bondre

Thanks man!

Collapse
 
bobbyiliev profile image
Bobby Iliev

This is a great article!

If you are a fan of opensource, feel free to contribute to the Introduction to Bash Scripting open-source eBook on GitHub!