Updated: March 2, 2009
In this article, I'm going to show you a number of simple, yet highly useful tricks that will make your scripting life easier. True, this article may appeal to the more geeky segment of my readers, but there's no reason to stop reading. As usual, I'll try to make my examples as simple and elegant as possible, with screenshots aplenty.
Scripts are an important part of Linux life. They are not necessary, but they can make your life easier by automating tasks. Most users will get along fine without ever bothering with scripts. But if you're lazy and would like your system to work hard rather than the other way around, learning how to write and use scripts is a good exercise.
BASH stands for Bourne Again SHell, the default Linux shell. If you're wondering what shell is, then think of its an interpreter between you (the user) and the system. You type in commands and the systems translates them into actual work. Most of the time, the results of this work are printed on your screen, inside the terminal.
Writing scripts is BASH can be as simple as entering commands one after another, then pasting them all into a text file that will become a script. Still, there are few tricks that can make a lot of difference. Let's see them.1. Backward compatibility
If you're planning on using your scripts only on your computer, then you probably do not need to worry much. But what if you intend to use your scripts on other computers, running older versions of Linux, including old kernels like 2.2 or 2.4, Solaris machines, UNIX machines, and other strange beasts?
BASH is the default shell for Linux, but some machines might not have it. On the other hand, the Bourne SHell (or simply SH) is included everywhere. So, if you're writing multi-system scripts, always go for the lowest common denominator, the SH. Thus, your scripts should begin like this:
What's the big difference?
Well, there are many, the way arrays are handled and commands substituted to name a few. For instance, command substitution in BASH is declared like this:
While SH recognizes the following syntax:
If you were to use the first format in SH, you'd get an error. BASH does work with backtick (`), though.
2. Return values from functions
BASH has a limitation. Return values from functions can only be up to 255. This means that if your function is supposed to calculate a tricky number, like 383, you would get a funny, unexpected result.
This applies both to exit and return commands.
Here's a little exercise you can try at home:
function does_nothing {
return $1 }
$(does_nothing $1); echo $?
Run this script several times, each time providing a different input each time, something like ./does_nothing 56, ./does_nothing 88 and so forth, until you pass the 255 barrier. Then, you'll see the 8-bit magic at work.
So obviously, using functions and return values this way is impractical for any sort of serious mathematics. Let's see how we can evade this problem.
Global and local variables
Yes, you guessed right. We use global and local variables and we declare functions a little differently. Here's a simple example where we take the input and add 1 to it, then print it back to the user.
number=0
manipulate() {
number=$(($1+1))
}
manipulate $1
echo $number
We used a global variable and we overrode its initial value. If you want to keep the initial number, but still perform some clevel arithmetics:
number=0
manipulate() {
local number=$(($1+1))
echo "Local number is $number"
}
manipulate $1
echo $number
Here, after the function was executed, our variable number remains untouched. This way, we also evade the return value limitation.
BTW, a crude way of getting past the exit code in BASH is to use negative values. Linux, as the operating system, uses exit value 0 as the sign of successful completion of execution and all other positive numbers as errors. Negative values are unused, so you may want to sneak in. Remember, this is ugly, but it is a possibility. The use of global and local variable is much more sensible. Which brings us to our next tip:
3. Always initialize variables
You don't want to guess what is going to happen during the execution of the script. Leave no doubt and prevent random assignments of values - and thus horribly confusing results and consequent mistakes - by assigning initial values to your variables.
4. Follow execution of your scripts
While creating your scripts, it's always useful to follow the execution, especially if you have errors. This is similar to placing break points in the code (e.g. Matlab, VBA), so you can keep track of your variables and arguments passed.
To do this, you need to declare your scripts a little differently from what you're used to. Just add -x to the header:
And this is how it looks:
5. Nested functions
You can call functions from one another. This is useful if you have complicated scripts that have to do lots of independent tasks, but rely on the results from intermediate steps to work properly. Here's a very stupid example:
6. Internal documentation
This is one of the most important things. Make sure your scripts include at least a few lines of comments, explaining what it is your script does. You should also carefully detail what each function does, especially what its input and output are.
Code without comments is not going to be read by anyone but the creator - and even then, only very close to the date of the creation. After several months, not even you will recall what you intended there on line 69. For all practical purposes, if you require any changes, you'll have to start from scratch.
Conclusion
While this article is not going to make you into a BASH king, it should make your life a little easier. The few points presented here are definitely valid for compliant, orderly work with scripts.
Backward compatibility, initialization of variables and careful debugging of script execution are always useful, no matter what language / shell you work with. Internal documentation is also critical, especially if you share your work. In the long run, the correct use of these small, simple tips (call them tools if you will) can help you better understand your own work and become more productive.
That's about it. Happy scripting.
Enjoy!