Situatie
Bugs and typos in Linux Bash scripts can do dire things when the script is run. Here are some ways to check the syntax of your scripts before you even run them.
- Those Pesky Bugs
Writing code is hard. Or to be more accurate, writing bug-free non-trivial code is hard. And the more lines of code there are in a program or script, the more likely it becomes that there will be bugs in it. The language you program in has a direct bearing on this. Programming in assembly is much tougher than programming in C, and programming in C is more challenging than programming in Python. The more low-level the language you’re programming in, the more work you have to do yourself. Python might enjoy in-built garbage-collection routines, but C and assembly certainly don’t.
Writing Linux shell scripts poses its own challenges. With a compiled language like C, a program called a compiler reads your source code—the human-readable instructions you type into a text file—and transforms it into a binary executable file. The binary file contains the machine code instructions that the computer can understand and act upon.
The compiler will only generate a binary file if the source code it’s reading and parsing obeys the syntax and other rules of the language. If you spell a reserved word—one of the command words of the language—or a variable name incorrectly, the compiler will throw an error.
For example, some languages insist you declare a variable before you use it, others are not so fussy. If the language you’re working in requires you to declare variables but you forget to do that, the compiler will throw a different error message. As annoying as these compilation-time errors are, they do catch a lot of problems and force you to address them. But even when you’ve got a program that has no syntactical bugs it doesn’t mean there are no bugs in it. Far from it.
Bugs that are due to logical flaws are usually much harder to spot. If you tell your program to add two and three but you really wanted it to add two and two, you won’t get the answer you expected. But the program is doing what it has been written to do. There’s nothing wrong with the composition or syntax of the program. The problem is you. You’ve written a well-formed program that doesn’t do what you wanted.
- Testing Is Difficult
Thoroughly testing a program, even a simple one, is time-consuming. Running it a few times isn’t enough; you really need to test all execution paths in your code, so that all parts of the code are verified. If the program asks for input, you need to provide a sufficient range of input values to test all conditions—including unacceptable input.
For higher-level languages, unit tests and automated testing help to make thorough testing a manageable exercise. So the question is, are there any tools that we can use to help us write bug-free Bash shell scripts?The answer is yes, including the Bash shell itself.
- Using Bash To Check Script Syntax
The Bash -n
(noexec) option tells Bash to read a script and check it for syntactical errors, without running the script. Depending on what your script is intended to do, this can be a lot safer than running it and looking for problems.
Here’s the script we’re going to check. It isn’t complicated, it’s mainly a set of if
statements. It prompts for, and accepts, a number representing a month. The script decides which season the month belongs to. Obviously, this won’t work if the user provides no input at all, or if they provide invalid input like a letter instead of a digit.
#! /bin/bash read -p "Enter a month (1 to 12): " month # did they enter anything? if [ -z "$month" ] then echo "You must enter a number representing a month." exit 1 fi # is it a valid month? if (( "$month" < 1 || "$month" > 12)); then echo "The month must be a number between 1 and 12." exit 0 fi # is it a Spring month? if (( "$month" >= 3 && "$month" < 6)); then echo "That's a Spring month." exit 0 fi # is it a Summer month? if (( "$month" >= 6 && "$month" < 9)); then echo "That's a Summer month." exit 0 fi # is it an Autumn month? if (( "$month" >= 9 && "$month" < 12)); then echo "That's an Autumn month." exit 0 fi # it must be a Winter month echo "That's a Winter month." exit 0
This section checks whether the user has entered anything at all. It tests whether the $month
variable is unset.
if [ -z "$month" ] then echo "You must enter a number representing a month." exit 1 fi
This section checks whether they have entered a number between 1 and 12. It also traps invalid input that isn’t a digit, because letters and punctuation symbols don’t translate into numerical values.
# is it a valid month? if (( "$month" < 1 || "$month" > 12)); then echo "The month must be a number between 1 and 12." exit 0 fi
All of the other If clauses check whether the value in the $month
variable is between two values. If it is, the month belongs to that season. For example, if the month entered by the user is 6, 7, or 8, it is a Summer month.
# is it a Summer month? if (( "$month" >= 6 && "$month" < 9)); then echo "That's a Summer month." exit 0 fi
If you want to work through our examples, copy and paste the text of the script into an editor and save it as “seasons.sh.” Then make the script executable by using the chmod
command:
chmod +x seasons.sh
We can test the script by
- Providing no input at all.
- Providing a non-numeric input.
- Providing a numerical value that is outside the range of 1 to 12.
- Providing numerical values within the range of 1 to 12.
In all cases, we start the script with the same command. The only difference is the input the user provides when promoted by the script.
./seasons.sh
That seems to work as expected. Let’s have Bash check the syntax of our script. We do this by invoking the -n
(noexec) option and passing in the name of our script.
bash -n ./seasons.sh
This is a case of “no news is good news.” Silently returning us to the command prompt is Bash’s way of saying everything seems OK. Let’s sabotage our script and introduce an error.
We’ll remove the then
from the first if
clause.
# is it a valid month? if (( "$month" < 1 || "$month" > 12)); # "then" has been removed echo "The month must be a number between 1 and 12." exit 0 fi
Now let’s run the script, first without and then with input from the user.
./seasons.sh
The first time the script is run the user doesn’t enter a value and so the script terminates. The section that we’ve sabotaged is never reached. The script ends without an error message from Bash.
The second time the script is run, the user provides an input value, and the first if clause is executed to sanity-check the user’s input. That triggers the error message from Bash. Note that Bash checks the syntax of that clause—and every other line of code—because it doesn’t care about the logic of the script. The user isn’t prompted to enter a number when Bash checks the script, because the script isn’t running.
Leave A Comment?