Bash scripting for beginners
This guide begins with useful terminal commands before introducing the basics of scripting with Bash.
This guide was migrated from the tutorial "Beginners/BashScripting" on the old Ubuntu community wiki.
Tidying
Commands
During your time as an Ubuntu user you will use the terminal to perform tasks such as
- Creating folders
- Deleting files
- Deleting folders and their sub-folders
- Opening applications as root
- Backing up your files
- Backing up your folders
- Checking system performance
- Check Devices
- Checking wireless connection
Along with many other things, the list above will be the commands we will discuss.
Creating folders
On Desktop, folders can be created by right clicking in the Nautilus file manager and selecting 'Create Folder'..
To do the same from the terminal, run:
mkdir /home/joe/Desktop/new_folder
The mkdir (make directory) command creates the folder and the file path tells it where to create the folder.
Deleting files and folders
Deleting files are done with the rm (remove) command:
rm /home/joe/file_to_be_deleted
The command you are about to read can potentially (if used incorrectly) destroy your system!
rm -r /home/joe/useless_Parent_folder
To force (note most of the time you will not need to use -f)
rm -rf /home/joe/useless_Parent_folder
This command is slightly different to the one before, it uses two options '-r' which means recursive (will delete the folder and all sub-folders) and '-f' means force (will not ask for your permission). This command is perfectly fine for deleting a directory (or "dir") and all its sub-dirs.
Warning
rm -rf /*
rm -rf /
Running commands as root
When working on the command line, you usually want to work with the default permissions. This way you ensure that you won't accidentally break anything belonging to the system or other users, so long as the system and other users haven't altered their file permissions.
There will be times when you wish to copy a file to a system folder (like /usr/local/bin) to make it available to all users of the system. Only the system administrator (i.e. the user 'root') should have permission to alter the contents of system directories like /usr/local/bin. For this reason, trying to copy a file (like a program downloaded from the Internet) into that folder is forbidden by default.
cp Downloads/some_downloaded_program /usr/local/bin
cp: cannot create regular file `/usr/local/bin/some_downloaded_program': Permission denied
Since it would be very tedious to always login as root to do administrative work (in fact you should avoid logging in as root with a graphical desktop) you can use sudo to execute a single command as root.
sudo cp Downloads/some_downloaded_program /usr/local/bin
The default Ubuntu installation is configured so that the user who was created during system installation is allowed to use this command. It will prompt for the password and execute the command as the root user.
Opening GUI applications as root
Sometimes you will want to edit a config file in your root folder, in order to save changes to this file you need root privileges, so you need to open a text editor as root. All you will need is just sudo:
sudo gedit
Backing up your files
To create a backup of a file, you can use the cp (copy) command:
cp source_file dest_file
This will copy the source file to the destination file.
Using the previous example, if you want to backup /path/to/conf_file.txt, run:
sudo cp /path/to/conf_file.txt /path/to/conf_file.txt.old
But what if you want to copy the file to another directory? That's just as easy. Let's say instead of copying /path/to/conf_file.txt to the /path/to/ directory, you want to copy it to a directory where you store all of your backup files, say /my/backup/folder/. In order to accomplish this you would type:
cp /path/to/conf_file.txt /my/backup/folder/ #saves conf_file.txt to /my/backup/folder/
cp /path/to/conf_file.txt /my/backup/folder/conf_file_new_name.txt
Backing up directories
To backup one directory to another, use cp -r (recursive):
cp -r /directory/to/be/copied/ /where/to/copy/to/
So if you wanted to copy all of the contents of our /path/to/ folder to the /my/backup/folder:
cp -r /path/to/ /my/backup/folder/foldername #foldername can be whatever you want the foldername to be
Command reference
For a full command reference, refer to the terminal command reference page.
Scripting
Tip
Bash is primarily a scripting language, so it would be a crime not to talk about scripting. Let's dive straight in with a bash script. More precisely the infamous "Hello World" script. You can create a bash script by opening your favorite text editor to edit your script and then saving it. Typically a script is saved with a .sh file extension, but is not necessary.
# !/bin/bash
echo "Hello, World"
The first line of the script defines which interpreter to use. Note that there is no leading whitespace before #!/bin/bash. To run a bash script, you first have to have the correct file permissions. We do this with chmod:
chmod a+x /where/i/saved/it/hello_world.sh #Gives everyone execute permissions
chmod 700 /where/i/saved/it/hello_world.sh #Gives read,write,execute permissions to the Owner
This will give the file the appropriate permissions so that it can be executed.
Next, open a terminal and run the script like this:
/where/i/saved/it/hello_world.sh
Hopefully you should have seen it print "Hello, World" to your screen. If so well done! That is your first Bash scrip
Tip
pwd, you will see the directory that you are currently working in (pwd stands for 'print working directory'). If your current working directory is /where/i/saved/it/, then you can shorten the above command to:
prompt$ pwd
/where/i/saved/it
prompt$ ./hello_world.sh
Variables
Variables basically store information. You set variables in a Bash script like this:
var="FOO"
The var can be anything you want as long as it doesn't begin with a number.
To access the information from the variable you need to put a $ in front of it like this:
var="FOO"
echo $var
Try entering those lines into a terminal one at a time; you will see that the first one just gives you another prompt and the second one prints FOO.
But that's all a bit boring. So let's make a script to ask the user for some information and then echo that information:
# !/bin/bash
clear
echo "Please enter your name"
read name
echo "Please enter your age"
read age
echo "Please enter your sex. Male/Female"
read sex
echo "So you're a $age year old $sex called $name"
The read allows the user to input information where it is then stored in the variable defined after the read. So, read variable would take whatever input the user entered and store it in $variable. We then access this with echo and set up a neat sentence. This script is reasonably messy though; read has another function that could halve the size of this script.
clear
read -p "Please enter your name : " name
read -p "Please enter your age : " age
read -p "Please enter your sex. Male/Female : " sex
echo "So you're a $age year old $sex called $name"
That is more efficient code. However, it's still a bit messy when run. A solution? Echo some white space:
clear
read -p "Please enter your name : " name
echo ""
read -p "Please enter your age : " age
echo ""
read -p "Please enter your sex. Male/Female : " sex
echo ""
echo "So you're a $age year old $sex called $name"
Now we have an efficient and clean Bash script.
If statements
An if statement can be used to check for something and do something else depending on the outcome of the check. For example, if I had an 'apple', I would want to make sure it's still an 'apple' and not an 'orange' because I don't like oranges!
The syntax for an if statement is
if [something]
then
elif
then
elif
then
....etc....
else
fi
The else if statement or (elif) is not necessary, but it can be used if needed.
An if statement to check if our $fruit variable is an 'apple' would look like this:
echo "Please enter type of fruit"
read fruit
if [ $fruit = apple ]
then echo "Good, I like Apples"
else echo "Oh no, I hate Oranges!"
fi
To explain this statement using pseudo code:
if [ the contents of $fruit is 'apple' ]
then say "Good, I like Apples"
if it's not, then say "Oh no, I hate Oranges!"
finish
Say you wanted to have 4 or 5 checks, the answer may be to write 4 or 5 statements but that's not the most practical way. This is where elif comes in handy.
if [ $fruit = apple ]
then echo "Good, I like Apples"
elif [ $fruit = pear ]
then echo "Good, I like Pears"
elif [ $fruit = banana ]
then echo "Good, I like Bananas"
else echo "Oh no, I hate Oranges!"
fi
This saves us from from repetitive scripting. There are better ways to check what the fruit is, but we won't go into that now.
Storing application stdout to a variable
Application stdout (what you see on the terminal screen, with an un-piped application) can be saved and used in Bash. The simplest and most elegant way is to use command substitution, by wrapping the code in $(...).
Example
fooVar=$(who)
echo $fooVar
This code should output the current users, their respective ttys, and date of login. Note that this strips newlines. Be sure to do any parsing in-line ( | grep, etc ) and then pass it to a variable. We will try this again, but grep for tty7, the GUI's tty.
Example 2
fooVar=$(who | grep tty7)
echo $fooVar
This should output the single user that is currently logged in.
Functions
Bash lets you create a function on the fly, really handy if you plan on using a code block more then once. Functions reduce the amounts of editing you have to do in a script, if and when you have to update your script.
Example
Here is an example script:
echo "echo is Called"
echo "Functions are FUN!"
echo "echo is Called"
Although this example is simple, you can see that if you want to change "echo is Called" to say "foo is Called" you would have to edit twice.
Below is the same app using functions.
echoFunction() {
echo "echo is Called"
}
fooBar() {
echo "Functions are FUN!"
}
echoFunction;
fooBar;
echoFunction;
The script is longer now, but if you want to change the echo call, you have to edit one line, not two. It's more maintainable.
Debugging
It can be useful to trace a script to find out why something does not work as expected. To trace, start it through Bash explicitly and use the -x option, like so:
bash -x ./script.sh
This will write each command to standard error (preceded by a ‘+ ’) before it is executed.
Other Scripting Languages related to Bash
tr
This is one of the most basic applications to pipe data through that uses a basic scripting syntax. In this case, it accepts regular expressions. Let's do a normally complicated task, transforming a string to all uppercase.
read foo
var=$(echo $foo | tr "{a-z}" "{A-Z}")
# {a-z} Matches a through z
# {A-Z} matches A through Z
echo $var
The output should look something like this:
this is a test
THIS IS A TEST
We can also use tr to translate strings, so let's translate all "tar" in $foo to "bar":
echo "Type in: I love tars"
read foo
var=$(echo $foo | tr "t" "b")
echo $var
The output should look something like this:
I love tars
I love bars
AWK
AWK is short for Aho, Weinberger & Kernighan.
It has its own custom scripting language, suitable for a page all by itself, so here we will only look at some basics I will cover only the basics that can help when you are bash scripting.
pidof clone
Let's make a quick pidof clone that prompts for a process identifier, then echoes the process ID:
read pname
ps -ef | grep -v grep | grep $pname | awk '{print $2}'
Let's take some pipes out and use only awk for the filtering:
read pname
ps -ef | awk -v p=${pname} '$8 ~ p { print $2 }'
Single quotes are used to pass the awk command(s). The curly braces are to use the awk language (for prints, ifs, etc.). Print prints the column passed given by the $ markup, space delimited.
The -v option allows passing a shell value into an awk variable, the $8 is a field variable (column 8 of the ps -ef command's output) and the operator ~ is a regular expression match.
There are a lot more commands than the print command, including if statements, and it is worth looking into if you are interested in using awk.
SED
Only the s/ command for substitutions with sed will be covered here.
Try this out to show that sed can not only replace in-line, but, unlike tr, can replace a string with a longer or shorter string than before:
read foo
echo $foo | sed "s/foo/bars/"
When this command is run, it should substitute the first appearance of "foo" with "bars".
This is an example of the output.
I love to go to foo
I love to go to bars