Bash script tutorial
Let’s create our first simple shell script
|
|
- The first line tells Unix that the file is to be executed by
/bin/sh
. This is the standard location of the Bourne shell on just about every Unix system. If you’re using GNU/Linux, /bin/sh is normally a symbolic link to bash (or, more recently, dash). - The second line begins with a special symbol:
#
. This marks the line as a comment, and it is ignored completely by the shell. - The only exception is when the very first line of the file starts with
#!
(shebang) - as ours does. This is a special directive which Unix treats specially. It means that even if you are using csh, ksh, or anything else as your interactive shell, that what follows should be interpreted by the Bourne shell. - Similarly, a Perl script may start with the line
#!/usr/bin/perl
to tell your interactive shell that the program which follows should be executed by perl. For Bourne shell programming, we shall stick to#!/bin/sh.
- The third line runs a command:
echo
, with two parameters, or arguments - the first is"Hello"
; the second is"World"
. - Note that
echo
will automatically put a single space between its parameters. - To make it executable, run
chmod +rx <filename>
Variables
Let’s look back at our first Hello World example. This could be done using variables. Note that there must be no spaces around the “=
” sign: VAR=value
works; VAR = value
doesn’t work. In the first case, the shell sees the “=
” symbol and treats the command as a variable assignment. In the second case, the shell assumes that VAR must be the name of a command and tries to execute it.
|
|
- This assigns the string “Hello World” to the variable
MY_MESSAGE
thenecho
es out the value of the variable. - Note that we need the quotes around the string Hello World. Whereas we could get away with
echo Hello World
because echo will take any number of parameters, a variable can only hold one value, so a string with spaces must be quoted so that the shell knows to treat it all as one. Otherwise, the shell will try to execute the commandWorld
after assigningMY_MESSAGE=Hello
- The shell does not care about types of variables; they may store strings, integers, real numbers - anything you like.
We can interactively set variable names using the read
command; the following script asks you for your name then greets you personally
|
|
Escape Characters
Certain characters are significant to the shell; for example, that the use of double quotes ("
) characters affect how spaces and TAB characters are treated, for example:
|
|
So how do we display: Hello "World"
?
$ echo "Hello \"World\""
The first and last " characters wrap the whole lot into one parameter passed to echo
so that the spacing between the two words is kept as is. But the code:
$ echo "Hello " World ""
would be interpreted as three parameters:
- “Hello "
- World
- "”
So the output would be
Hello World
Note that we lose the quotes entirely. This is because the first and second quotes mark off the Hello and following spaces; the second argument is an unquoted “World” and the third argument is the empty string; “”.
Loop
For Loop
|
|
|
|
The output of the above code is
|
|
This is well worth trying. Make sure that you understand what is happening here. Try it without the *
and grasp the idea, then re-read the Wildcards
section and try it again with the *
in place. Try it also in different directories, and with the *
surrounded by double quotes, and try it preceded by a backslash (\*
)
While Loop
|
|
|
|
The colon (:
) always evaluates to true; whilst using this can be necessary sometimes, it is often preferable to use a real exit condition. Compare quitting the above loop with the one below; see which is the more elegant. Also think of some situations in which each one would be more useful than the other:
|
|
This reads the file “myfile.txt
”, one line at a time, into the variable “$input_text
”. The case
statement then checks the value of $input_text
. If the word that was read from myfile.txt
was “hello” then it echo
es the word “English”. If it was “gday” then it will echo Australian
. If the word (or words) read from a line of myfile.txt
don’t match any of the provided patterns, then the catch-all “*” default will display the message “Unknown Language: $input_text” - where of course “$input_text” is the value of the line that it read in from myfile.txt
.
A handy Bash (but not Bourne Shell) tip I learned recently from the Linux From Scratch
project is:
mkdir rc{0,1,2,3,4,5,6,S}.d
instead of the more cumbersome:
|
|
And ls
can be done recursively, too:
|
|
Test
Test is used by virtually every shell script written. It may not seem that way, because test
is not often called directly. test
is more frequently called as [
. [
is a symbolic link to test
, just to make shell programs more readable. It is also normally a shell builtin (which means that the shell itself will interpret [
as meaning test
, even if your Unix environment is set up differently):
|
|
This means that ‘[
’ is actually a program, just like ls
and other programs, so it must be surrounded by spaces:
|
|
will not work; it is interpreted as if test$foo = "bar" ]
, which is a ‘]
’ without a beginning ‘[
’. Put spaces around all your operators. I’ve highlighted the mandatory spaces with the word ‘SPACE’ .
Note: Some shells also accept “
==
” for string comparison; this is not portable, a single “=
” should be used for strings, or “-eq
” for integers.
Test is a simple but powerful comparison utility. For full details, run man test
on your system, but here are some usages and typical examples.
Test is most often invoked indirectly via the if
and while
statements. It is also the reason you will come into difficulties if you create a program called test
and try to run it, as this shell builtin will be called instead of your program! The syntax for if...then...else...
is:
|
|
Also, be aware of the syntax - the “if [ ... ]
” and the “then
” commands must be on different lines. Alternatively, the semicolon “;
” can separate them:
|
|
You can also use the elif
, like this:
|
|
This will echo "Something"
if the [ something ]
test succeeds, otherwise it will test [ something_else ]
, and echo "Something else"
if that succeeds. If all else fails, it will echo "None of the above"
.
Case
The case
statsement saves going through a whole set of if ... then ... else
statements. Its syntax is really simple:
|
|
Variables 2
The first set of variables we will look at are $0 ... $9
and $#
. The variable $0
is the basename of the program as it was called. $1...$9
are the first 9 additional parameters the script was called with. The variable $@
is all parameters. The variable $*
is similar, but does not preserve any whitespace and quoting, so “File with spaces” becomes “File”, “with”, and “spaces”. $#
is the number of parameters the script was called with.
|
|
The othere two main variables set are $$
and $!
. These are both process numbers. The $$
variable is the PID of the currently running shell. This can be useful for creating temporary files, such as /tmp/my-script.$$
which is useful if many instances of the script could be run at the same time, and they all need their own temporary files.
The $!
variable is the PID of the last run background processd. This is useful to keep track of the process as it gets on with its job.
Another interesting vardfiable is IFS
. This is the Interfal Field Separator. The default value is SPACE TAB NEWLINE
, but if you are changing it, it’s easier to take a copy as shown:
|
|
Functions
|
|