After a brief DNS issue, we are back online. In addition, I'm back to coding again.
So, without further delay, let's get back on topic here. :)
One of the many things I've been doing lately is writing some bash scripts to automate server installs. I've based my scripts on the instructions found at http://howtoforge.com/perfect_setup_ubuntu_6.10. I had need to do this multiple times, and got irritated with entering the commands over and over and over. So I wanted to let the computer do it for me - afterall, that's what they're for aren't they?
But one of the things I wanted to do was separate my tasks and allow the user running my script to choose which task to execute. To do this we need a way to isolate our tasks into meaningful blocks. One method would be to create multiple script files and call them as needed. However, anyone with a coding background understands that this is what FUNCTIONS are for. And as it turns out, Bash supports functions.
A function is specified like this:
myfunction () {
#statements go here
}
And to call the function you simply specify the function name:
# . . .
myfunction
# . . . more statements
When the code hits the function call, execution is branched to the function until it is done, and then continues from the next statement after the function call. Just like any other programming language.
But there's a problem here - what about passing data to the function? There's two parts to this, passing the parameters, and then processing the parameters.
# calling the function with parameters
#assuming the variables were previously defined
myfunction $param1 $param2
myfunction () {
echo $1
echo $2
}
As you can see, we pass our parameters to the function by simply stating the value we'd like to pass, and when we have multiple values we separate them with a space.
Then to process the parameters, we identify them as numbered variables - $1 and $2 above.
Now that we are able to create functions, we can now create our menu.
I've seen two methods for creating menus. The first uses the select command. This takes a list of items to be displayed, and will present a number list for you to choose from. When you enter a number, the item is placed into a variable that you can then process. For example:
#!/bin/bash
select CHOICE in bob amy quit
do
case "$CHOICE" in
"bob")
echo "Bob was here"
;;
"amy")
echo "Amy was here"
;;
"quit")
exit
;;
esac
done
This code presents our menu, then loops until the quit option is selected. Here's a sample of the output:
~$ ./select
1) bob
2) amy
3) quit
#? 1
Bob was here
#? 2
Amy was here
#? 3
~$
This is great for quick and easy menus. However when your items need to include spaces, you start running into problems.
Another option for menus is to create your own with echo statements, and then use the read to gather the user input. For example we may emulate the above sample like this:
#!/bin/bash
showMenu () {
echo "1) bob"
echo "2) amy"
echo "3) quit"
}
while [ 1 ]
do
showMenu
read CHOICE
case "$CHOICE" in
"1")
echo "Bob was here"
;;
"2")
echo "Amy was here"
;;
"3")
exit
;;
esac
done
This presents the same menu, in a slightly different manner. We use an endless while loop to continue over the menu until we force an exit. Each time through the loop we call our showMenu function, and then use the read command to place the user input into the CHOICE variable. Next we check the value of the choice variable against a list of numbers and take the appropriate actions.
It should be noted that we do not need to use the numbers at all with this method. Instead, we could be checking to see if the word "quit" had been entered. This also means that the user must hit the Enter key to submit their choice. But the select method expects the same.
The truly useful part here is the read command. It supports a number of options. However, tracking down these options can be a little challenging - the word "read" being so common. I came across a nice description of the command at http://www.ss64.com/bash/read.html. (And other commands at http://www.ss64.com/bash/.) Specifically, you can ask the read command to automatically return after a specified number of characters has been entered (the -n option), use a prompt (the -p option) or the silent mode (the -s option) which is great when asking for passwords.
The above code also introduces the case construct which we have not seen before. However, it's usage should be apparent. The tricky part here is that each case begins with the case value, followed by the end bracket ")". Then any commands needing to be executed in that particular case. The case is ended by two semi-colons ";;". This structure allows us to minimize our code by avoiding a number of IF constructs.
With this knowledge, we are now able to create interactive scripts, and have a few more tools for branching the code execution in useful ways.