Tag Archives: shell scripts

An Introduction to Bash Scripting – Part 5

User input

User input can be achieved using the read command. Here is a quick example:

#!/bin/bash
echo        "**** user input ****"
echo -n "Please may I have some user input:"
read input
echo ""
echo "you typed in $input"

… and this will give us:

:~/bin$ nano input
:~/bin$ chmod u+x input
:~/bin$ input
**** user input ****
Please may I have some user input:hello
you typed in hello
:~/bin$

But what if I have more than one string to input?
We can input them like this:

#!/bin/bash
echo "**** user input ****"
echo -n "Please may I have some user input: "
read input1
echo -n "please sir, may I have another : "
read input2
echo -n "please sir, just one more : "
read input3
echo ""
echo "you typed in $input1 $input2 $input3"

So what else can we do with this? Well lets take our input and put it in a text file to create a list.  If we go back to the single input script and do this:

#!/bin/bash
echo "**** Tony's Basic ToDo Script ****"
echo ""
echo -n "what do you want to do next : "
read todo
echo $todo >>todo.lst

… and this creates a small todo list text file. Using functions and if statements we could add checks to see that we don’t duplicate items and give ourselves a way to view the file.

Final Notes:

This has been a very basic course, intended just to help get you started but there are a few final thoughts that are worth noting:

Going back to functions; there is an alternative method of denoting functions. I like to note my functions as functions, and often I will add comments just to make it plain what the function is, however there is an alternative way of specifying functions. If we go back to the example in the last post we can also write the function like this:

counter() {
let count=count+nmbr
}

You can see that the word function has been removed and the parenthesis are used to denote the function. Depending on the editor you are using, this can cause the label counter to be highlighted, making the file easy to read.

 

 

 

 

 

 

 

Tweet about this on TwitterShare on Google+Share on LinkedInShare on FacebookShare on RedditShare on StumbleUponEmail this to someone

An Introduction to Bash Scripting – Part 4

In this post we take a look at loops, functions and case statements.

Loops

Loops are useful for performing a task or tasks, until a criteria has been met (like a count down for instance). There are two basic types of loop that we can create: the for do or while do loop.

Why would we use these different types of loops and in what circumstances?

The for-do loop

A typical for-do loop may look a little like this (below). The loop is executed until the condition for exiting the loop is met.

#!/bin/bash
for arg in [list]
    do
        some commands
    done

For instance:

for file in `ls`
do
     echo "the file name is $file"
done
echo "script complete"

… will output the local file listing as such:

the file name is backup
the file name is count
the file name is input
the file name is lister

… and so on. You can see how the loop runs until the loop is out of file names from the ls command.

Also notice that the ls command is put in what we call back-ticks, (the unshifted tilda key on my keyboard). This tells the shell that the string is a command, to be executed in another shell – for old guys like me this would be analogous to a sub routine. If we just put ls is the loop, the shell does not recognize it as a command.

for file in `ls`
do
 echo "the file is $file" | tee -a output.txt
done
echo "script complete"

… and here I have added a pipe to the tee command which splits the standard output to the screen and to a file. The -a option appends the file, and in checking the output, we can see the file list on screen and in the file output.txt.

The while-do loop

A typical while – do loop may look something like this. The loop is executed as long as the given condition is true.

#!/bin/bash
count=0
while [ $count -lt 100 ];
do
        echo $count
        let count=count+1
done
echo ""
echo "script Complete"

The while-do is usually associated with a counter of some sort and this simple script counts to 100. Again, we can output to files and so forth as above:

count=0
while [ $count -lt 100 ];
do
 echo $count | tee -a output.txt
 let count=count+1
done
echo ""

Functions

Functions are pre-defined tasks that can be called once or multiple times, in different places in our code. We define functions as follows:

function function_name {
put the function code here
}

So for instance, if we go back to our count script, we can change the code as follows:

count=0
 function counter {
     let count=count+nmbr
 }
for nmbr in `cat output.txt`
 do
   counter
   echo "$nmbr, $count"
 done
echo "script complete"

You can see how the function counter gets called in the code.

Case Statements

Case statements are useful for a handling a number of different criteria and making decisions based on them; a scalable IF statement is a good way to consider the case statement. The syntax follows thusly:

 case [variable] in
    1)
        insert code here
    ;;
    2)
        insert code here
    ::
    *)
        insert catch all code here
    ;;
esac

For instance, the following script asks the user for an item, and then comments on it:

#!/bin/bash
echo "this script demonstrates the use of a case statement"
echo -n "give me an object: "
read object
echo ""
echo "you gave me $object"
case $object in
 car)
 echo "$object are fun"
 ;;
 trucks)
 echo "I like $object" 
 ;;
 *)
 echo "I don't know what $object is"
 ;;
esac
echo "script complete"

The script asks for the input variable object, and compares it to items that the script knows about (cars and trucks), and outputs a comment accordingly. The catch all end statement covers all other items that the script doesn’t know about.

this script demonstrates the use of a case statement
give me an object: trucks
you gave me trucks
I like trucks
script complete

And that brings us to user input.

Tweet about this on TwitterShare on Google+Share on LinkedInShare on FacebookShare on RedditShare on StumbleUponEmail this to someone

An Introduction to Bash Scripting – Part 3

In our previous posts we created a script and passed variable or arguments to the commands in the script. Next we will take a look at adding some logical decision making capabilities to the scripts.

Tests

We can do this with tests. The syntax for a test uses the [ and ] bracket, along with a flag to designate the test required.

flag conditions
-gt = greater than
-lt = less than
-ge = greater than equal to
-le = less than or equal to
-eq = equal to
-ne = not equalto
-f = is file
-d = is directory
-l =  is symlink

For example:

[ 2 -gt 1 ] is true because 2 is greater than 1. If we reverse this to [ 1 -gt 2 ] we get a false output, because the condition is not true.

if – then

The if then statement can also be used for decision making in script files.  The syntax is as follows:

if [test]
     then action
fi

The fi statement closes the if then clause.
Combine the tests with the if – then statement and we have a working decision making system.

Going back to our script again; lets assume that we don’t want the script to run UNLESS it has both arguments. How do we do that?

We can test our variables to see if they are empty like this:

if [ -z "$location" ]
then
        echo please supply a location
        exit
fi

exit will exit the script.

and we can do the same for $filename. Notice the quotes around the variable. Just to show that the tests work, I left off the filename:

~/bin$ lister /etc
please supply a filename
~/bin$

Now that we have successfully passed arguments to our script, lets do a little more with arguments. First of all, all arguments are labeled $1, $2, $3 etc as they are passed from the command line to the script.

If we use $# ; this equals the number of arguments we have for the script.

For example – myscript arg1 arg2 arg3 arg4

would give $# = 4.

We can use this to make sure we have the right number of arguments for the script to proceed.

~/bin$ nano myscript
~/bin$ cat myscript
#! /bin/bash
echo $#
~/bin$ myscript a b c d e f
6

… and you can see that 6 is the number of arguments.

Next we look at loops.

Tweet about this on TwitterShare on Google+Share on LinkedInShare on FacebookShare on RedditShare on StumbleUponEmail this to someone

An Introduction to Bash Scripting – Part 2

In the previous post, we looked at creating our first script and running it. Now lets do some more complicated things with scripts.

Variables

So now that we have our script working, what next? Well lets try doing something with variables. Variables (and perhaps the wider term arguments), are useful for providing our scripts with information that is not absolute and may change over time etc.

Lets create a script that creates an output file.  The use of >> means the file is appended. We could use > to create a new file each time the script runs.

~/bin$ nano lister
~/bin$ cat lister
#!/bin/bash
# lists directory contents and writes them to the file dir_list.txt
ls >> dir_list.txt
echo script complete
~/bin$ chmod u+x lister
~/bin$ lister
script complete
~/bin$ cat dir_list.txt
backup
dir_list.txt
lister
myscript
~/bin$

So you can see that I added a comment explaining what the script does, and an echo at the end to announce the script has completed.

Now lets take that same script and give the user the ability to input the directory to be listed. We have created a variable called location from the first input argument $1.

~/bin$ nano lister
~/bin$ cat lister
#!/bin/bash
# lists directory contents and writes them to the file dir_list.txt
location=$1
ls $location >> dir_list.txt
echo "script complete: listed the contents of $location"

… and we can pass that argument onto our commands in the script. Here we see it used twice – once for the ls command and once in the echo command.

~/bin$ lister /etc
script complete: listed the contents of /etc
~/bin$ cat dir_list.txt
acpi
adduser.conf
adjtime
alternatives
anacrontab
apm

and in the above we can see that I passed the script the input of /etc, and we can also see the output (truncated to keep this post shorter).

Now lets add a second variable for the output file name:

~/bin$ nano lister
~/bin$ cat lister
#!/bin/bash
# lists directory contents and writes them to the file dir_list.txt
location=$1
filename=$2
ls $location > $filename
echo "script complete: listed the contents of $location"
echo "##########################################"
echo "displaying contents of $filename"
echo "##########################################"
cat $filename
~/bin$ lister /etc files.txt
script complete: listed the contents of /etc
##########################################
displaying contents of files.txt
##########################################
acpi
adduser.conf

You can see, we have the file name and folder passed to the ls command in the script and the echo command to give us an output.

Variable Confusion

So what happens if we do this?

variable=hello
echo $variable
echo $variable12345
echo ${variable}12345

Out echo output is as follows:

hello

hello12345

The problem is that variable and variable12345 are not the same variable. To solve this problem we can use  { }.  The use of these (squiggly brackets) brackets (as opposed to these brackets or parenthesis) is to unambiguously identify the variable.

In the next post, we will look at applying some logic and decision making to our scripts.

Tweet about this on TwitterShare on Google+Share on LinkedInShare on FacebookShare on RedditShare on StumbleUponEmail this to someone

An Introduction to Bash Scripting – Part 1

This post is going to be a short course in bash scripting. This is aimed at Linux users. I am basing this course off the Linux Academy course on udemy.com.

I have divided this up as follows:

    • What is a Bash script?
    • Basic script structure
    • Variables
    • Logic and Testing
    • Loops, Functions and Case statements
    • User input

What is a Bash script?

As we delve into this answer, I am going to assume two things:

  1. You understand some basic Linux commands and are familiar with the command line interface
  2. You have a basic understanding of programming languages, what they look like and how they are structured

So what is a Bash script?
A script is a repeatable sequence of commands, and what I mean by that is that a script is a file, which contains a list of commands that are executed in a predictable and known order.  A program file if you like.

We write scripts for several reasons

  • to automate tasks
  • to save time
  • to decrease repetition

Lets say we have a task that we perform daily, and that task is always the same set of commands; we can write a script to run those commands, and in doing so, we automate that daily task.

We could add that task to the cron tab, having it run automatically when required (daily in this case).  It is these capabilities that make shell scripting so very powerful.

But, what about the ‘Bash” bit?

Bash ( Bourne Again SHell) is a Linux shell, which is a command line environment (or interpreter) that a Linux user can use to send instructions to the Linux Kernel. There are a number of shells in common usage (sh, bash, csh, zsh etc) but bash is probably the most well known and used. The shell (bash in this case) is what takes your instructions and passes them to the kernel for execution.

Basic Script Structure

In its simplest form, a script is a text file with a list of instructions. In order to make a text file with a list of instructions into a shell script, we have to do a few things:

1. Where is bash?

Bash is essentially a program that runs within Linux, and we can find that program by typing: which bash. In most cases we get the answer /bin/bash. Because it is a program, paths become important if we wish to run our scripts.

2. Tell Bash that this is a shell script

We also need to tell bash that we have a script and not just a list of commands. We do this by putting a marker at the beginning of the file. This is the #!/bin/bash (otherwise known as a shebang) with the path to the bash shell, so that Linux knows how to process this file.

3. Give the script permission to execute

When I create a script, by default, it is saved with our basic permission set, which does not include user execute permission  – because most of the time, user files are not executable. In this case we have to change the permission to allow user execution.

So lets make a script:
Remember the old days of BASIC (yeah I am that old) – lets do this:

$ echo hello world
hello world

Just like the old BASIC program:

10 PRINT “Hello World”
20 GOTO 10

and who didn’t love doing that …

Now lets put it in a script:

$ nano myscript
$ cat myscript
#!/bin/bash
echo Hello World

I like to use nano because I can never remember the commands for vi. I am lazy and should learn the vi commands, but I just don’t seem to be able to bring myself to do it.

Paths and the Environment

The chances are that you are using a user friendly flavor of Linux, like Ubuntu or similar and you created this file in your user directory.

Chances are that when you ran the file, you got something like this:

$ myscript
No command 'myscript' found, did you mean:
 Command 'pyscript' from package 'python-pyscript' (universe)
myscript: command not found

The problem here is the PATH environment variable. Path is what tells Linux where the programs are that you want to run. if you echo $PATH you will see all the paths that Linux looks in for programs, and your user directory is not one of them, so when you type your command (or in this case script filename), Linux can’t find it.

Also these new Linux versions tend not to install with a local /bin directory but where that directory is installed, the profile script that runs during login will have detected the bin directory and placed it in the PATH environment. With Ubuntu, if you create a /home/username/bin directory, logout and then log back in again, and put your script in there, it will run. Here is my path environment, and I have high lighted the local bin directory.

$ echo $PATH
/home/tony/bin:/opt/OpenPrinting-Gutenprint/sbin:/opt/OpenPrinting-Gutenprint/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

No now we need to set the permissions so that the script can run.

ls -al
-rw-rw-r-- 1 tony tony 30 Jan 29 10:42 myscript
chmod u+x myscript
ls -al
-rwxrw-r-- 1 tony tony 30 Jan 29 10:42 myscript

See the eXectable bit set in the permissions?

$ myscript
Hello World
$

And there it is! Our very first script.
Just another quick note – if you run this in another folder, you can run it this way: sh myscript. This negates the requirement for the PATH environment to be set, but it really isn’t that useful for continuous use. Or you can run it like this ./myscript will also work, or lastly, make a direct link to the file as in /home/user/script or what ever your path maybe.

But our script, as fun as it was to play with, is not a very useful script. Lets make something more useful. This uses the tar function to create a zipped backup file of my user directory. Note also that I can comment in the script using the # symbol.

~/bin$ nano backup
~/bin$ cat backup
#!/bin/bash
#
# This is a backup script
tar -cvzf backup.tar.gz /home/tony
# create verbose zip and file options.
cp $HOME/backup.tar.gz /home/tony/bin
# copy to the local bin folder
echo backup complete
~/bin$ backup
bash: /home/tony/bin/backup: Permission denied
~/bin$

So what happened? Well we need to change the permissions; so lets chmod u+x backup and try again.

I’m not going to print the output (it is after all verbose) but it works!

~/bin$ ls -al
total 5274788
drwxrwxr-x 2  tony tony       4096 Jan 29 13:39 .
drwxr-xr-x 40 tony users      4096 Jan 29 11:30 ..
-rwxrw-r-- 1  tony tony        207 Jan 29 13:33 backup
-rw-rw-r-- 1  tony tony 5401362432 Jan 29 13:44 backup.tar.gz

Now lets take a look at some more detail in part 2.

Tweet about this on TwitterShare on Google+Share on LinkedInShare on FacebookShare on RedditShare on StumbleUponEmail this to someone