This chapter is about errors: how to find them and how to fix them. No programmer I've ever known of is able to consistently create perfect programs. So don't feel bad if you also have some problems you need to solve. I've spent many hours looking for a missing closing bracket or a misspelled variable name.
There are two different types of errors: syntax errors and logic errors. Syntax errors are made as you type your script into an editor. For example, you might not add a closing quote or might misspell a filename. Logic errors are more insidious and difficult to find. For example, you might place an assignment statement inside an if statement block that belongs outside the block. Or you might have a loop that runs from 0 to 100 when it should run from 10 to 100. Accidentally deleting the 1 or not entering it in the first place is very easy.
Syntax errors are usually easy to fix. The section "Common Syntax Errors" discusses some common syntax errors. You'll see how to decipher some of Perl's error messages.
Logic errors can be very hard to fix. They are discussed in the section "Logic Errors." While there is no magic wand to wave over a program that will identify logic errors, there are some tools that can help-like the debugger. A debugger is an environment that lets you execute your program line by line. This is also called single-stepping through your program. You can also display or modify the value of variables. The debugger is discussed in the section "Stepping Through Your Script."
Perl is generally considered an interpreted language. However, this is not truly accurate. Before being executed, your script is compiled into an internal format-just like Java's byte-codes or Pascal's p-code. While Perl is compiling your program, it also checks for syntax errors. This is why syntax errors are also called compile-time errors.
Fixing syntax errors is a matter of reading the error message displayed by the compiler and then trying to understand which line of code generated the message and why. The next section, "Common Syntax Errors," might help. If you are uNCertain which line of code really generated the error, try commenting out the likely culprits. Then, re-execute your program and look at the error messages that are produced to see if they have changed.
One very common error is to use elseif instead of the correct elsif keyword. As you program, you'll find that you consistently make certain kinds of errors. This is okay. Everyone has his or her own little quirks. Mine is that I keep using the assignment operator instead of the equality operator. Just remember what your particular blind spot is. When errors occur, check for your personal common errors first.
This section shows some common syntax errors and the error messages that are generated as a result. First, the error message are shown and then the script that generated it. After the script, I'll cast some light as to why that particular message was generated.
Scalar found where operator expected at test.pl line 2, near "$bar" (Missing semicolon on previous line?) $foo = { } # this line is missing a semi-colon. $bar = 5;
Perl sees the anonymous hash on the first line and is expecting either an operator or the semicolon to follow it. The scalar variable that it finds, $bar, does not fit the syntax of an expression because two variables can't be right after each other. In this case, even though the error message indicates line 2, the problem is in line 1.
Bare word found where operator expected at test.pl line 2, near "print("This" (Might be a runaway multi-line "" string starting on line 1) syntax error at test.pl line 2, near "print("This is " String found where operator expected at test.pl line 3, near "print("" (Might be a runaway multi-line "" string starting on line 2) (Missing semicolon on previous line?) Bare word found where operator expected at test.pl line 3, near "print("This" String found where operator expected at test.pl line 3, at end of line (Missing operator before "); ?) Can't find string terminator '"' anywhere before EOF at test.pl line 3. print("This is a test.\n); # this line is missing a ending quote. print("This is a test.\n"); print("This is a test.\n");
In this example, a missing end quote has generated 12 lines of error messages! You really need to look only at the last one in order to find out that the problem is a missing string terminator. While the last error message describes the problem, it does not tell you where the problem is. For that piece of information, you need to look at the first line where it tells you to look at line two. Of course, by this time you already know that if the error message says line 2, the error is probably in line 1.
Can't call method "a" in empty package "test" at test.pl line 1. print(This is a test.\n); # this line is missing a beginning quote.
The error being generated here is very cryptic and has little
to do with the actual problem. In order to understand why the
message mentions methods and packages, you need to understand
the different, arcane ways you can invoke methods when programming
with objects. You probably need to add a beginning quote if you
ever see this error message.
Tip |
As long as you follow the object calling guidelines used in Chapter 14, "What Are Objects?," you will never have to worry about the more advaNCed ways to call object methods. |
This list of syntax errors could go on for quite a while, but you probably understand the basic coNCepts:
Errors are not always located on the line mentioned in the error message.
Errors frequently have nothing to do with the error message displayed.
These are the programming problems-sometimes called bugs-that you can stare at for hours without having a clue about why your script doesn't work. If you find yourself in this position, take a walk or eat some chocolate. In other words, take a break from staring at the computer screen. You can also find another programmer to walk through the code with you. Quite often while explaining the code to someone else, the problem becomes obvious.
Besides these two options, you can do the following:
Use the -w Command-line Option-This option will produce warning messages about questionable code.
Use the strict pragma-This pragma will force you to declare all variables before using them.
Use the built-in debugger-The built-in debugger will let you single-step through your script, examining or changing variable values as needed.
Each of these options is discussed in separate sections later.
As a general rule, when debugging logic errors it helps to break
complex expressions and statements into simpler ones: the simpler,
the better. Use temporary variables if you need to. If you use
the ++ or -
operators inside fuNCtion calls or complex expressions, don't.
Move the decrement or iNCrement operation to a separate line.
After the program is debugged, you can always recombine the simple
statements into complex ones.
Tip |
One of the most common logic problem is using the assignment operator (=) when you should use the equality operator (==). If you are creating a conditional expression, you'll almost always use the equality operator (==). |
One of the most important features to combat logic errors is the w command-line option, which causes warning messages to be displayed indicating questionable Perl code. Questionable code iNCludes identifiers that are mentioned only oNCe, scalar variables that are used before being set, redefined subroutines, refereNCes to undefined filehandles, and filehandles opened read-only that you are attempting to write on.
For example, can you find anything wrong with the following lines of code?
$foo = { }; $bar = 5; print("$foa\n"); print("$bar\n");
You probably can't see anything wrong at first glaNCe. In fact, this program compiles and runs without complaint. However, running this program with the w option (perl w test.pl) results in these error messages:
Identifier "main::foa" used only oNCe: possible typo at test.pl line 4. Identifier "main::foo" used only oNCe: possible typo at test.pl line 1. Use of uninitialized value at test.pl line 4.
With these error messages, the problem becomes obvious. Either
the variable name $foo is
misspelled in the assignment statement or the variable name $foa
was misspelled in the print statement.
Tip |
Always use the -w command-line option! Let me repeat this: Always use the -w command-line option! Okay? It will save you lots of headaches tracking down bugs that Perl can catch automatically. |
The -w option is so useful that you should always use it. If you know that a specific line of code is going to generate an error message and you want to ignore it, use the $^W special variable. For example,
$foo = { }; $bar = 5; $^W = 0; print("$foa\n"); print("$bar\n"); $^W = 1;
eliminates the display of the Use of
uninitialized value at test.pl line 4. error message.
Unfortunately, this technique will not stop all messages, and
the placement of the $^W
= 0;
statement seems to affect whether the message will be suppressed.
Caution |
This feature did not seem to be too stable in my version of Perl. If you can't get it to work in your version, don't spend too much time trying to find the problem. It simply may not work properly in your version of Perl, either. |
In the last chapter, "Modules," the use of modules to implement pragmas was discussed. One very useful pragma to aid in debugging is use strict;. This statement does two things:
Tip |
The strict directory on the CD holds all listings from Chapter 8converted so they work with the use strict; pragma: essentially, all the variables needed to be declared local using the my() fuNCtion. |
When the strict pragma is used, your script will not compile if the preceding two rules are violated. For example, if you tried to run the following lines of code,
use strict; $foo = { }; $bar = 5; print("$foo\n"); print("$bar\n");
you would receive these error messages:
Global symbol "foo" requires explicit package name at test.pl line 3. Global symbol "bar" requires explicit package name at test.pl line 4. Global symbol "foo" requires explicit package name at test.pl line 6. Global symbol "bar" requires explicit package name at test.pl line 7. Execution of test.pl aborted due to compilation errors.
In order to eliminate the messages, you need to declare $foo and $bar as local variables, like this:
use strict; my($foo) = { }; my($bar) = 5; print("$foo\n"); print("$bar\n");
I bet you already have guessed that the my() fuNCtion makes the variables local to the main package.
In the next section, you see how to use the debugger to step through your programs.
So far, you've read about how to limit the possibility of errors appearing in your programs. If, after using the -w and the strict pragma, you still have a problem, it's time to use the debugger.
What is the debugger? Quite simply, it is an interactive environment that allows you to execute your script's statements one at a time. If necessary, you can display the lines of your script, view or alter variables, and even execute entirely new statements.
You start the debugger by using the -d command-line option. The following line
perl -w -d 08lst08.pl
starts the debugger and loads the script called 08lst08.pl. If you want to invoke the debugger with no script, you need to perform a small bit of magic, like this
perl -d -e "1;"
to start debugger without any program. I say that this is a bit of magic because you haven't read about all the different command-line options available for the Perl interpreter. You see them all in Chapter 17, "Using the Command-Line Options." The -e option tells Perl to execute a single Perl statement. In this case the statement is 1;, which basically means do nothing. It does, however, stop the interpreter from looking for the name of a script file on the command line.
When the debugger starts, your screen will look something like this:
Loading DB routines from $RCSfile: perl5db.pl,v $$Revision: 4.1 $$Date: 92/08/07 18:24:07 $ Emacs support available. Enter h for help. main::(08lst08.pl:3): my($codeRef); DB<1>
This message tells you that the debugger (DB) routines have been loaded. The DB<1> is a prompt that indicates that the debugger is waiting for input. The line number inside the angle brackets is the current execution line. The current execution line is that line that the debugger waits to execute.
One of the features of the debugger is the capability to insert breakpoints into your script. A breakpoint is an instruction that tells the debugger to stop, to display a prompt, and to wait for input. When the debugger first starts, there are no breakpoints defined for your program. See the section "Examples: Using Breakpoints" later in the chapter for more information.
You can use any of the commands listed in Table 16.11 while using
the debugger. While some of the commands are demonstrated in the
sections that follow the table, you can't hurt anything by experimenting
with any or all of the commands on your own.
Command | Description |
Commands That Control Actions | |
This command tells the debugger to perform ACTION just before the current execution line is executed. Optionally, you can specify a line number. For example, a 10 print("$numFiles"); executes the print statement before line 10 is executed. If line 10 is inside a loop, the action is performed each time through the loop. | |
Deletes all actions. | |
Lists all breakpoints and actions. | |
Forces the debugger to execute ACTION each time the debugger prompt is displayed. This command is great if you need to print the value of certain values each time you are prompted by the debugger. | |
Forces the debugger to execute ACTION after every debugger command you issue. | |
Commands That Involve Breakpoints | |
Sets a breakpoint at the current execution line. You can specify a line where the breakpoint should be set. For example, b 35 sets a breakpoint at line 35. You can also create a conditional breakpoint. For example, b 35 $numLines == 0 causes the debugger to stop at line 35 only if $numLines is equal to zero. Watch conditions can also be attached to fuNCtions; just use the fuNCtion name instead of a line number. | |
Deletes the breakpoint from the current execution line. If you specify a line number, the breakpoint is deleted from that line. | |
Deletes all breakpoints. | |
Lists all breakpoints and actions. | |
Commands That Display Information | |
Lets you print out parts of your script. There are several flavors of this command that you can use:
Using a plain l displays about 10 lines of your script. | |
Lists all breakpoints and actions. | |
Prints the result of evaluating EXPR to the display. It is a shorthand way of saying print DB::OUT (EXPR). | |
Lists all fuNCtion names that are defined. The list will iNClude any fuNCtion defined in modules as well as those in your script. | |
Prints a stack trace. A stack trace displays a list of fuNCtion calls and the line number where the calls were made. | |
Lists all variables that are currently defined from all packages and modules that are loaded. A better form of this command is V PACKAGE or V PACKAGE VARLIST where PACKAGE is the name of a loaded package or module, and VARLIST is a currently defined variable in PACKAGE. When specifying variable names, don't use the $, @, or % type specifiers. | |
Displays about 10 lines centered around LINE. For example, if you use w 10, lines 7 to 16 might display. | |
Lists all variables in the current package. If you have stepped into a fuNCtion that is in package foo, the variables in package foo are displayed, not those in main. You can also specify exactly which variables to display if needed. When specifying variable names, don't use the $, @, or % type specifiers. | |
Displays about 10 lines of your script that are before the current line. For example, if the current display line is 30, this command might display lines 19 to 29. | |
Commands That Control Execution | |
Steps through the lines in your script one at a time. It steps into any user-defined fuNCtion that is called. While single-stepping is slow, you see exactly how your code is being executed. | |
Executes the next statement in your script. Although all fuNCtion calls are executed, it does not follow the execution path inside a fuNCtion. This command enables you to move quicker through the execution of your script than simply using the s command. An example of this is shown in the "Examples: Using the n Command" section later in this chapter. | |
Executes the rest of the statements in the current fuNCtion. The debugger pauses for input on the line following the line that made the fuNCtion call. | |
Executes the rest of the statements in your script unless a breakpoint is found before the script ends. You can optionally use this command to create a temporary break by specifying a line number after the c. I think of this command as continue until LINE. | |
Pressing the Enter key without specifying a command will make the debugger repeat the last n or s command that was used. This feature makes it a little easier to single-step through your script. | |
Commands That Work with the Debugger Command History | |
Re-executes the previous command. You can also specify the number of the previous command to execute. Use the H command to get a list of the previous commands. If you specify a negative number, like ! -2, the debugger counts backwards from the last executed command. | |
Lists all the debugger commands you have issued. Only commands that cause action are saved in the command history. This means that the l and T commands are not saved. You can limit the history viewed by specifying a negative number. For example, H -5 displays the last five commands you have issued. | |
Miscellaneous Commands | |
Causes the debugger to switch to FILENAME. The file specified must have already been loaded via the use or require statements. Please note that some of the documentation that accompanies the Perl interpreter may indicate that f is the finish command. It used to be; however, the finish fuNCtionality is now accomplished by the r command. | |
Quits the debugger. You can also use the Ctrl+D key sequeNCe under UNIX and the Ctrl+Z key sequeNCe under DOS and Windows. | |
Toggles trace mode on and off. Trace mode, when on, displays each script line as it is being executed. I don't recommend this option except for very short programs because the lines are displayed so quickly that you won't be able to read them. | |
Searches for pattern in the currently loaded file. If pattern is found, the current display line is changed to the line where pattern was found. | |
Searches backward for pattern in the currently loaded file. If pattern is found, the current display line is changed to the line where pattern was found. | |
Displays any aliases that are currently defined. You can also use it to create aliases. See the section "Examples: Creating Command Aliases" later in this chapter for more information about aliases and the = command. | |
Any text that is not recognized as an alias or a debugger command is executed as a Perl statement. See the section "Examples: Using the Debugger as an Interactive Interpreter" later in this chapter for more information about executing Perl statements inside the debugger. |
As you can see, the debugger has quite a few commands to choose from, and it is very powerful. Most programmers will not need all of the fuNCtionality that the debugger has. If you learn to display script lines, to use breakpoints, and to display variables, you'll be well on your way to solving any logic problem that may arise.
The debugger uses the coNCept of a current display line. The current display line is simply the last line that has been displayed by the l command. When the debugger first starts, the current display line is the first executable line. See Listing 16.1 for some examples.
Listing 16.1 16LST01.PL-Using the Debugger List Commands
01: package Inventory_item; 02: sub new { 03: } 04: 05: package Pen; 06: @ISA = (Inventory_item); 07: 08: sub new { 09: } 10: 11: package Color; 12: print("Executing Color statements\n"); 13: $colors{"blue"} = "Die Lot 13"; 14: $colors{"red"} = "Die Lot 5"; 15: 16: sub new { 17: } 18: 19: package main; 20: print("Executing main statements\n");
Note |
This listing is identical to Listing 14.5 except that the guts of the fuNCtions have been removed. This was done simply to shorten the listing. |
If you load this script into the debugger (perl -d 16lst01.pl), you will see that the first displayed line is line 6. The lines before line 6 are package and fuNCtion statements. Line 6 will also be the current execution line.
If you issue the l debugger command, lines 6 to 15 are displayed:
6: @ISA = (Inventory_item); 7: 8: sub new { 9: } 10: 11: package Color; 12: print("Executing Color statements\n"); 13: $colors{"blue"} = "Die Lot 13"; 14: $colors{"red"} = "Die Lot 5"; 15:
After this display, the current display line is changed to 15, but the current execution line is still line 6. If you issue the l debugger command again, lines 16 to 20 are displayed.
You can display the first line of your script by using the l 1 debugger command. This command displays the first line of the script and changes the current display line:
1: package Inventory_item;
Because this script uses package names to change the namespace in which the fuNCtions are defined, simply issuing l new does not display a new() fuNCtion. Instead, you need to use the double-colon (::) notation to specify which namespace to use. For example, l Color::new displays
16: sub new { 17: }
While inside the debugger, you can use the X and V commands to view variables. These commands are very good for simple variables, but I have not found them to be useful for complex data structures. For example, Listing 16.2 shows a small program that creates an array within an array data structure.
Listing 16.2 16LST02.PL-Using the X Command to View Arrays
sub prtArray { my(@array) = @_; my($index) = 0; foreach (@array) { if (ref($_) eq 'ARRAY') { my($innerIndex) = 0; foreach (@{$array[3]}) { print("\t$innerIndex\t'$_'\n"); $innerIndex++; } } else { print("$index\t'$array[$index]'\n"); } $index++; } } @array = (1, 2, 3, [1, 2, 3], 4); # an array inside an array. 1;
Note |
This listing is for illustrative purposes only. The crude method used to print the data structure is not recommended for practical use. I suggest that you invest time creating a general-use routine that can print more than one type of complex structure. You might also look at the dumpvars module that comes with most, if not all, Perl distributions. |
Load this script into the debugger (perl -d 16lst01.pl), use the s command to execute the array assignment, and then display @array with the X array command. Your display should look like this:
@array = ( 0 '1' 1 '2' 2 '3' 3 'ARRAY(0x7c693c)' 4 '4' )
You can see that the displayed values are not as informative as you might hope for because of the array refereNCe in element 3. However, because the prtArray() fuNCtion is designed to print this type of data structure, call it from the debugger using the prtArray(@array); command. This should result in a display like this:
0 '1' 1 '2' 2 '3' 0 '1' 1 '2' 2 '3' 4 '4'
The 1; line of code is used to let you execute the array assignment without the debugger ending. Just ignore it.
The n command lets you step over fuNCtion calls in your scripts. This command saves you time because you won't need to single-step through every line of every fuNCtion. The program in Listing 16.3 has three fuNCtions defined and three fuNCtion calls and is used to demonstrate the n command.
Listing 16.3 16LST03.PL-Using the n Command to Step Over FuNCtion Calls
1: sub a { 2: print("This is fuNCtion a\n"); 3: } 4: 5: sub b { 6: print("This is fuNCtion b\n"); 7: } 8: 9: sub c { 10: print("This is fuNCtion c\n"); 11: } 12: 13: a(); 14: b(); 15: c();
First, let's see the regular path of execution that takes place using the s command:
13: a(); 2: print("This is fuNCtion a\n"); This is fuNCtion a 14: b(); 6: print("This is fuNCtion b\n"); This is fuNCtion b 15: c(); 10: print("This is fuNCtion c\n"); This is fuNCtion c
If the n command is used instead of the s command, the path of execution stays the same. However, you are prompted after each fuNCtion call. The lines inside the fuNCtion are still executed, however.
13: a(); This is fuNCtion a 14: b(); This is fuNCtion b 15: c(); This is fuNCtion c
By switching between the s and n commands, you can decide which fuNCtions to step into and which to step over.
Breakpoints are used to tell the debugger where to stop execution of your script. After the execution is stopped, the debugger prompts you to enter a debugger command. For example, you might want to set a breakpoint on a line that assigns a complicated expression to a variable. This allows you to check any variables used in the expression before it is executed.
Listing 16.4 demonstrates the different breakpoint commands you can use.
Listing 16.4 16LST05.PL-Sample Program to Test Breakpoints
1: sub a { 2: my($foo) = @_; 3: 4: print("This is fuNCtion a. Foo is $foo.\n"); 5: } 6: 7: a(10); 8: a(5);
When the script is first loaded into the debugger, the current execution line is 7. Using the c command causes the entire program to be executed. A transcript of the debugging session might look like this:
main::(16lst04.pl:7): a(10); DB<1> c This is fuNCtion a. Foo is 10. This is fuNCtion a. Foo is 5.
You can force the debugger to stop each time that a() is invoked by using the b a command. This lets you examine the @_ parameter array before the fuNCtion is started. For example:
main::(16lst04.pl:7): a(10); DB<1> b a DB<2> c main::a(16lst04.pl:2): my($foo) = @_; DB<3> p @_ 10 DB<4> c This is fuNCtion a. Foo is 10. main::a(16lst04.pl:2): my($foo) = @_; DB<4> p @_ 5 DB<5> c This is fuNCtion a. Foo is 5.
Tip |
The p command, used in this example, is shorthand for the statement print("@_\n");. You can use the p command to print any variable. |
You can also create conditional breakpoints. For example, you could tell the debugger to stop inside a() only if $foo is equal to 5 using the command b 4 $foo == 5. In this instaNCe, you can't use b a $foo == 5 because $foo is a local variable. When the debugger stops just before executing a fuNCtion, the parameter array is initialized but not any of the local variables. A debugging session using conditional breakpoints might look like this:
main::(16lst04.pl:7): a(10); DB<1> b 4 $foo == 5 DB<2> L 4: print("This is fuNCtion a. Foo is $foo.\n"); break if ($foo == 5) DB<2> c This is fuNCtion a. Foo is 10. main::a(16lst04.pl:4): print("This is fuNCtion a. Foo is $foo.\n"); DB<2> c This is fuNCtion a. Foo is 5.
The debugger did not stop during the first call to a() because $foo was equal to 10. On the second call, $foo is set to 5 which causes the debugger to stop.
The L debugger command is used to display all breakpoints and their conditions. If you don't specify any conditions, a default condition of 1 is supplied. Because 1 is always true, this creates an uNConditional breakpoint. If you had created an uNConditional breakpoint on line 7, the L command would display the following:
4: print("This is fuNCtion a. Foo is $foo.\n"); break if ($foo == 10) 7: a(10); break if (1)
The d command is used to delete or remove breakpoints. Issuing the commands d 4 and then L would result in this display:
7: a(10); break if (1)
If you want to delete all the breakpoints at oNCe, use the D command.
The = command is used to create command aliases. If you find yourself issuing the same long command over and over again, you can create an alias for that command. For example, the debugger command
= pFoo print("foo=$foo\n");
creates an alias called pFoo. After this command is issued, typing pFoo at the debugger prompt produces the same results as typing print("foo=$foo\n");.
You use the = command without any arguments when you want a list of the current aliases.
If you want to set up some aliases that will always be defined, create a file called .perldb and fill it with your alias definitions. Use the following line as a template:
$DB::alias{'pFoo'} = 'print("foo=$foo\n");';
After you create this file and its alias definitions, the aliases will be available in every debugging session.
In Chapter 13, "Handling Errors and Signals," you learned how to create an interactive Perl interpreter that could replace shell and batch files. The program was shown in Listing 13.3. You can also use the debugger as an interactive interpreter. In fact, it does an even better job in some cases.
If you create a script with fuNCtions that perform individual system tasks, you can run that script inside the debugger. Then you can call the fuNCtions from the debugger command lines as needed. Listing 16.5 shows what one possible script might look like.
Listing 16.5 16LST05.PL-A Script with Some System MaintenaNCe FuNCtions
sub printUserReport { # read list of users # determine usage statistics # display report } sub backupUsers { # remove backup file. #'delete /user/*.bak' # backup user files to tape. #'\backup /user/*'; } sub help { print("\n"); print("backupUsers will perform the nightly backup.\n"); print("printUserReport will display user usage statistics.\n"); print("\n"); } 1;
Note |
This script is really nothing but a skeleton. You should be able to flesh it out with fuNCtions that are useful to you. |
You load this script into the debugger with the command perl -d 16lst05.pl. After the script loads, you can run any of the fuNCtions by typing their name at the debugger prompt. Here is a sample debugger session:
main::(16lst05.pl:22): 1; DB<1> help backupUsers will perform the nightly backup. printUserReport will display user usage statistics. DB<2> backupUsers DB<3> q
I think there is a certain art to debugging that only experieNCe can teach. There are so many different places where things can go wrong that it's impossible to remember which bug is most likely to appear in a given scenario. If you have lived through the frustration of tracking a bug for hours only to have someone look at your program for three minutes and say, "Look, that minus sign should be a multiplication sign!" you are much more likely to find the bug the next time. There is no substitute for real-life debugging.
Let's recap what you did learn in this chapter. You started out by reading about syntax or compile-time errors. This class of error involved a misplaced parenthesis, a missing quote, or some other slip of the fingers while entering your program into an editor. Syntax errors are found when Perl compiles your program into an internal format prior to actually executing it. The only way to track down a syntax error is to read the error messages and look at your program.
Logic errors, on the other hand, can be harder to find. They involve some logical flaw in your program. Using the index into an array or specifying the wrong variable as a parameter to a fuNCtion both qualify as logic errors.
The first step to combating logic errors is to use the -w command-line option. The -w command tells Perl to display warning messages for various dangerous coding practices.
The next step is to use the strict pragma in your programs. This requires that you declare every variable you use. Creating only local variables minimizes the possibility of inadvertently changing the wrong variable or causing side effects in your program.
If you still have logic errors after these two options have been used, you might use the debugger. The debugger lets you single-step through your program and print or modify variables. You can also set breakpoints or actions, and you can interactively call any fuNCtion directly from the debugger command line.
The next chapter discusses all the Perl command-line options. You'll also read more about the -e option mentioned earlier.
Answers to Review Questions are in Appendix A.