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"
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
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")'
$ ./script.sh
Hello Javascript
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"
$ ./script.sh
Hello, Bash
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"
$ ./script.sh
What is you name: godcrampy
Hello godcrampy
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"
$ ./script.sh
Enter an action: cook
You are cooking
4. Comments
# Comments start with hash
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]
$ ./script.sh first second
./script.sh
first
second
first second
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
Note that [
is actually a command. Try running $ where [
.
test
, [
and [[
are almost similar. There are a few subtle differences.
And & Or
[[ ... ]] && [[ ... ]] # And
[[ ... ]] || [[ ... ]] # Or
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
# 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
# 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
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
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
${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
continue
andbreak
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)
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
Insert
arr[5]=e # direct address and insert/update
arr=(${arr[@]:0:1} new ${arr[@]:1}) # Adding 'new' to array
Delete
arr=(a b c d)
unset arr[1]
echo << "${arr[1]}" # Outputs nothing
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
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
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
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!
Top comments (19)
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.
This sounds like something I definitely need! Thanks!
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
Thanks, that's really useful!
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.
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 π
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.
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!
on the begining of the tutorial, i had to use
chmod +x ./script.sh
instead ofchmod -x ./script.sh
to make it executable, am I missing something?Yup, yours is correct. I added the
-
out of habit! Fixed. Thanks πThanks for this fun handzone π
**small typo in section 6
[[ -n "$str" ]] # True if string is not of length zero
fixed!
Thanks great article.
I'm glad! And thank-you for reading!
ππ½ππ½ππ½
This is really useful, you saved me a lot of time. Also, great posts ππ»ππ».
Thanks man!
A well written and concise crash course! This is a great diving board into other shell scripting topics like simple automation.
Thanks π