Computer Science Canada WIP - Bash Whirlwind |
Author: | wtd [ Fri Oct 13, 2006 3:35 pm ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Post subject: | WIP - Bash Whirlwind | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hello, bash! Let's take a look at the simplest possible bash script.
The first line (and in the case only) starts with a pound sign, so it's a comment. However, this comment also has a special purpose as the first line in the script. It indicates the program which is to be used to execute the script. Now we'll look at a script which says hello. Oddly enough, we'll call it hello.sh.
The echo program simply spits back the arguments sent to it. The exclamation mark is escaped with a backslash. To make this executable, we simple need to use:
Hello... you Let's greet someone by name, as passed to the script via the command-line parameters.
The $1 in the above is referring to a special variable. These numbered variables hold the arguments passed to the script. Perhaps if we create a variable called "name" to refer to this, it would make things clearer.
A bit of error-checking What if some sneaky user just called:
And provided no name? Well, then we'd get:
That's no good. The "name" variable is basically an empty string, which when used simply results in, well... again nothing. So let's test for that.
Greet two names So now that we've greeted one name, it should be simplicity itself to greet two.
Perhaps this would be easier, though, if we had that code to greet a name wrapped up somehow in one place. I suppose we could define a function.
So, the question is... what's going on here? Well, I define a function named greet. Functions take their arguments in the same way as scripts. So inside I create a local "name" variable to refer to that argument. The variable is made local so that it doesn't interfere with any "name" variables outside of the function. I then have only to call that function twice. Greet any number of people! Now that we've done one name, and two names, why not any number of names? The arguments to a script get stored in the numbered variables, but the whole collection of them get stored in the $@ special variable. We just have to loop over that to greet everyone.
Is that an array? In order to answer that, you really have to understand that it's all about strings in shell scripting. The "$@" variable is not an array. Rather it's a string that contains all of the arguments passed to the program. If we called hello.sh as:
Then "$@" contains:
We can then see the loop in the above code as:
As the shell parses spaces as separators, it sees foo, bar and baz as separate values. Had our loop been written a bit differently, using quotes to group text, these values would be seen as only a single value.
Then the loop becomes:
Choosy greet Let's make our greet function a tad more selective.
The "-o" is the "or" operator. However, we can maybe do this a bit more nicely.
But then, since the patterns in "case" structures are just regular expressions, we can make this a bit nicer.
More error checking As our script stands, if no names are entered, then nothing happens. What if we change that a bit so it tells us that isn't proper use of the script? Let's use the special variable $# (which holds the argument count) and the "less than" operator to do just that.
Greeting a complicated name This should be a short section. Let's say I want to greet "Bob Smith."
This happens because the shell parser sees Bob and Smith as two entirely separate arguments. We can use quotes, though, to rectify this situation.
It should also be known that this applies to the "echo" program that we have used extensively. It takes multiple arguments, and echoes them out, separated by a single space.
That does what we expect, but if we introduce multiple spaces...
We can only retain that formatting by using quotes and making this a single argument.
Redirection Let's say I want to print the output of the script to a file, instead of the console.
Let's append another run of the file, not overwriting the old results.
Now, there's a problem.
Did we really want the error message getting redirected out to the file? Probably not. If there was an error, we wanted to see it, and leave the file empty.
To get this outcome, we need to redirect our error message to standard error. By default, it gets echoed to standard output. We can redirect that standard output to standard error, though.
Or alternatively...
A small note: semi-colons You may have noticed me using semi-colons and wondered if they were just part of the syntax for loops and conditionals. They are not. They simply allow us to separate lines without using a newline.
Could be written as:
Or even:
|