There are several very elementary techniques you can use to debug your scripts.
Switch Your Shell to Execute (Debug) Mode
Remember the Order of Operations? You can change the mode of the Bourne/bash/Korn shell to show you what your command looks like after the replacement operations are performed by commanding:
sh -x # substitute "bash" or "ksh" as necessary
You have just opened a sub-shell. You must type “exit” to leave it.
When you run a command, wildcards are replaced, variables are expanded, embedded commands are run, and then the final resulting command is actually sent to the Bourne/bash/Korn interpreter. You’ll see this resulting command, after you press the Enter key, on the line below your command, preceeded by a + sign.
You can type:
sh +x
to turn “off” debugging, but what you’ve actually done is open yet another subshell. You’ll need to command “exit” to leave this one too.
Checking Your Return Value (Error Code)
The most basic way to test for the success of a single command is to examine its error code. The error code is always returned whenever you run anything; it’s usually just not checked. Your job, then, is to check it.
First, run a command:
ls /bin
Then check its return value with this command:
echo $?
You can examine this value one command at a time, or you can include these tests within a script:
cat foo # this is "command 1", and can be any command if [ $? -ne 0 ]; then echo "Error at command 1!" # every time you use this code block, increment the number # so you can tell where errors occurred else echo "command 1 worked" # comment out the line above after development fi
This is especially valuable when you’re passing variables around:
echo "$myvar" # this is "command 2" in this case if [ $? -ne 0 ]; then echo "Error at command 2! \$myvar doesn't exist." else echo "command 2 worked" # comment out the line above after development fi
Echoing Values During Development
It’s also highly useful to check the values you’re passing around by echoing them back during testing.
#!/bin/bash echo -n "Username: " read username # test the value of username echo "username is $username" # then comment this line out later if [ $username = root ]; then echo "Welcome to FooSoft 3.0" foosoft.sh else echo "You must be root to run this script" fi
Creating Your Own Error Codes
Even more useful is designating your own error codes. Yes, you do get to do that: you’re not just stuck with “0” or “1”.
#!/bin/bash echo -n "Username: " read username # test the value of username echo "usernameis $username" # then comment this line out later if [ $username = root ]; then echo "Welcome to FooSoft 3.0" foosoft.sh else echo "You must be root to run this script" exit 666 fi
Note that exit 666. If you use this, and test your error code:
echo $?
you’d get this if an invalid user tried to log in:
666
Here’s the ultimate step: building debugging into your script. Near the top of your script, define and set a variable:
debugging=1 # set to 0 to turn off debugging
Then, at frequent intervals in your code, trigger messages telling you the value of variables, your position in the routine or any other information:
#!/bin/bash echo -n "Username: " read username debugging=1 if [ $debugging -eq 1 ]; then echo "Value of \$username is " $username else # bash will complain if you don't do at least # something in an else # though an else is not technically necessary here echo -n "" fi
Now you can switch your debug information on and off at a single location, by changing the value of the debugging variable.