Shell Script Debugging Technology

Source: Internet
Author: User

I. Preface

Shell programming is widely used in the Unix/linux world, and mastering shell programming is a must for a good unix/linux developer and system administrator. The main task of script debugging is to find out the cause of the script error and locate the line where the error occurred in the script source code, the commonly used methods include parsing the output error message, by adding debug statements in the script, outputting debugging information to assist in diagnosing errors, using debugging tools and so on. However, compared with other high-level languages, Shell interpreter lacks the corresponding debugging mechanism and debugging tool support, the output error message is often very unclear, beginners in debugging scripts, in addition to know with the Echo statement output some information, there is no other method, and only rely on a large number of ECHO statements to diagnose errors, It's really a lot, so it's common for beginners to complain that shell scripts are too difficult to debug. This article will systematically introduce some important shell script debugging techniques, hoping to benefit the shell beginner.

The target audience for this article is the developer, tester and system administrator in the Unix/linux environment, requiring the reader to have basic shell programming knowledge. The examples used in this article are tested under Bash3.1+redhat Enterprise Server 4.0, but the debugging techniques should also apply to other shells.

Two. Output debugging information in shell scripts

By adding debug statements to the program, it is the most common debugging means to display information about key areas or where errors occur. Shell programmers often use the echo (Ksh programmer often uses print) statements to output information, but relying solely on the output trace of the Echo statement is cumbersome, and the large number of ECHO statements that are added to the script during the debug phase will have to be removed one by one of the time when the product is delivered. To solve this problem, this section mainly introduces some methods of how to easily and effectively output debugging information.

1. Using the trap command

The trap command is used to capture the specified signal and execute a pre-defined command. The basic syntax is: trap ' command ' signal where signal is the signal to be captured, and command is the order to be executed after capturing to the specified signal. You can use the Kill-l command to see all the available signal names in the system, and the command executed after capturing the signal can be any one or more valid shell statements, or a function name. When the shell script executes, it produces three so-called "pseudo-signals", which are called "pseudo-signals" because the three signals are generated by the shell and other signals are generated by the operating system, and it is helpful to debug by capturing the three "pseudo-signals" using the trap command and outputting the relevant information.

Table 1. When the shell pseudo signal name produces exit exit from a function or the entire script executes ERR when a command returns a non-0 state (on behalf of the command execution is unsuccessful) before each command in the DEBUG script executes

By capturing the exit signal, we can output some of the variables we want to track when the shell script aborts execution or exits from the function, and thus determine the execution state of the script and the cause of the error, using the trap ' command ' EXIT or trap ' command ' 0

By capturing the err signal, we can easily trace the execution of unsuccessful commands or functions, and output relevant debugging information, here is a sample program to capture the Err signal, where $lineno is a shell built-in variable that represents the current line number of the shell script.      $ cat-n exp1.sh 1 errtrap () 2 {3 echo "[line:$1] Error:command or function exited with status $?"      4} 5 foo () 6 {7 return 1; 8} 9 Trap ' Errtrap $LINENO ' ERR ten ABC one foo

During debugging, in order to keep track of the values of certain variables, we often need to insert the same echo statement in many parts of the shell script to print the values of the related variables, which is cumbersome and clumsy. By capturing the debug signal, we only need a trap statement to complete the full tracking of the relevant variables.

Here is an example program that tracks variables by capturing the debug signal: Cat-n exp2.sh 1 #!/bin/bash 2 trap ' echo ' before execute line: $LINENO, a= $a, b= $b,     c= $c "' DEBUG 3 a=1 4 If [" $a "-eq 1] 5 then 6 b=2 7 Else 8 B=1 9 fi Ten c=3 echo "End"

It is clear from the running results that the value of the relevant variable changes after each command execution. At the same time, from the running results of the printed line number to analyze, you can see the entire script execution trajectory, can determine which conditions branch execution, which conditional branches are not executed.

2. Using the tee command

Pipelines and input and output redirects are used very much in shell scripts, and the execution of some commands is directly the input of the next command under the action of the pipeline. If we find that the execution result of a batch of commands connected by a pipeline is not as expected, it is necessary to step through the execution of each command to determine where the problem is, but because the pipeline is used, these intermediate results are not displayed on the screen and are difficult to debug, so we can use the Tee command now.

The tee command reads the data from the standard input, outputs its contents to a standard output device, and saves the content as a file. For example, the following script fragment, which is to get the IP address of the machine:

Ipaddr= '/sbin/ifconfig | grep ' inet addr: ' | Grep-v ' 127.0.0.1 ' | Cut-d:-f3 | The entire sentence after the awk ' {print '} ' #注意 = sign is enclosed in anti-quotation marks (the key to the left of the number 1 key). Echo $ipaddr

Run this script, the actual output is not the native IP address, but the broadcast address, then we can use the tee command, output some intermediate results, the above script fragment is modified to:

Ipaddr= '/sbin/ifconfig | grep ' inet addr: ' | Grep-v ' 127.0.0.1 ' | Tee Temp.txt | Cut-d:-f3 | awk ' {print $} ' echo $ipaddr

After that, the script is executed again, and then the contents of the Temp.txt file are viewed:

$ cat Temp.txt inet addr:192.168.0.1 bcast:192.168.0.255 mask:255.255.255.0

We can see that the second column of the intermediate result (separated by the: number) contains the IP address, and the third column is intercepted using the cut command in the script above, so we just need to change the cut-d:-f3 in the script to cut-d:-f2 can get the correct result.

Specific to the script example above, we may not need the help of the tee command, for example, we can segment the various commands connected by the pipeline and view the output of each command to diagnose the error, but in some complex shell scripts, These commands that are connected by pipelines may also depend on some of the other variables defined in the script, which makes it much more cumbersome to run each command at the prompt, and simply inserting a tee command between the pipes to see the intermediate results is more convenient.

3. Use the "Debug hooks"

In the C language program, we often use debug macros to control whether or not to output debug information, and in shell scripts we can also use such a mechanism, as shown in the following code:

If ["$DEBUG" = "true"]; Then echo "debugging" #此处可以输出调试信息 such blocks of code are often called "Debug hooks" or "Debug blocks". Inside the debug hook can output any of the debugging information you want to output, the advantage of using debug hooks is that it can be controlled by the debug variable, in the development and debugging stage of the script, you can first execute the Export debug=true command to open the debug hook, so that it output debugging information, And when the script is delivered to use, there is no need to bother to delete debug statement one by one in the script.

If you use the IF statement to determine the value of the debug variable in every place where you need to output debugging information, it is still cumbersome to define a debug function to make the process of inserting the debug hooks more concise and convenient, as shown in the following code:

$ cat-n exp3.sh 1 DEBUG () 2 {3 If ["$DEBUG" = "true"]; then 4 [email protected] 5     Fi 6} 7 a=1 8 DEBUG echo "a= $a" 9 if ["$a"-eq 1] ten then one b=2 b=1 fi debug echo "b= $b" c=3-debug echo "c= $c" in the Debug function shown above, any commands passed to it are executed, and this The execution process can be controlled by the value of the debug variable, we can call all debug-related commands as parameters of the debug function, very convenient.

Three. Using the shell's execution options

The debugging method described in the previous section is to locate the error by modifying the source code of the shell script so that it outputs relevant debugging information, does it have a way to debug the shell script without modifying the source code? The answer is to use the shell's execution options, and this section describes the usage of some common options:

-N reads only the shell script, but does not actually perform the-X entry trace, which shows each command executed by-C "string" reads the command from the strings

"-n" can be used to test for a syntax error in a shell script, but does not actually execute the command. After shell scripting is complete, it is a good practice to first use the "-n" option to test the script for syntax errors before it is actually executed. Because some shell scripts will have an impact on the system environment, such as generating or moving files, or if the syntax errors are found in actual execution, you will have to do some manual recovery of the system environment to continue testing the script.

The "-C" option causes the shell interpreter to read and execute shell commands from a string rather than from a file. You can use this option when you need to temporarily test the execution results of a small piece of script as follows: Sh-c ' A=1;b=2;let c= $a + $b; echo "C= $c"

The "-X" option can be used to track script execution and is a powerful tool for debugging shell scripts. The "-X" option causes the shell to display each command line that it actually executes during the execution of the script, and displays a "+" sign at the beginning of the row. The "+" sign is followed by the contents of the command line after the variable substitution, helping to analyze what commands were actually executed. The "-X" option is simple and easy to handle for most shell debugging tasks, and should be used as a preferred debugging tool.

If you combine the trap ' command ' debug mechanism described earlier in this article with the "-X" option, we can output both the actual execution of each command and the value of the relevant variable line by row, which is useful for debugging.

Continue with the exp2.sh described earlier, and now add the "-X" option to execute it:

$ sh-x exp2.sh + trap ' echo ' before execute line: $LINENO, a= $a, b= $b, c= $c "' DEBUG + + echo ' before execute Line:3, a=,b=,c= ' Before execute line:3, a=,b=,c= + a=1 + + echo ' before execute line:4, a=1,b=,c= ' before execute line:4, a=1,b=,c= + ' [' 1-eq 1 ' + + echo ' before execute line:6, a=1,b=,c= ' before execute line:6, a=1,b=,c= + b=2 + + echo ' before execute line : Ten, a=1,b=2,c= ' before execute line:10, a=1,b=2,c= + c=3 + + echo ' before execute line:11, A=1,b=2,c=3 ' before execute Lin E:11, a=1,b=2,c=3 + echo end end

In the above result, the line preceded by the "+" is the command that the shell script actually executes, and the line preceded by the "+ +" sign executes the command specified in the trap mechanism, while the other rows are output information.

The shell's execution options can be specified in the script, in addition to being specified when the shell is started. Set-parameter means that an option is enabled, and set + parameter means that an option is turned off. Sometimes we don't need to use the "-X" option at startup to keep track of all the command lines, so we can work with the set command in our script, as shown in the following script fragment:

Set-x #启动 the "-X" option to track the program segment set +x #关闭 the "-X" option

The set command can also be invoked using the debug hook-debug function described in the previous section, which avoids the hassle of deleting these debug statements when script delivery is used, as shown in the following script fragment:

Debug Set-x #启动 "-X" option to track the program segment DEBUG set +x #关闭 "-X" option

Four. Enhancements to the "-X" option

The "-X" execution option is currently the most common means of tracking and debugging shell scripts, but the output of debugging information is limited to the actual execution of each command after the variable substitution and a "+" prompt at the beginning of the line, incredibly even the row number is not important, for the debugging of complex shell scripts , is still very inconvenient. Fortunately, we can use some of the shell's built-in environment variables to enhance the output information of the "-X" option, which introduces several shell-built environment variables:

$LINENO represents the current line number of the shell script, similar to the built-in macro in C __line__

The name of the $FUNCNAME function, similar to the built-in macro __func__ in C, but the macro __func__ can only represent the current function name, and $funcname is more powerful, it is an array variable containing all the functions on the entire call chain name, so the variable ${ Funcname[0]} represents the name of the function that the shell script is currently executing, and the variable ${funcname[1]} represents the name of the function that called the function ${funcname[0]}, and the remainder can and so on.

$PS 4 main prompt variable $PS1 and second-level prompt variable $ps2 are common, but few people notice the effect of the fourth-level prompt variable $PS4. We know that using the "-X" execution option will show each command that was actually executed in the shell script, and that the value of $PS4 will be displayed in front of each command output by the "-X" option. In the bash shell, the default value of $PS4 is the "+" sign. (Now that you know why the "-X" option is used, the output command has a "+" sign in front of it?) )。

Using the $PS4 feature, we can enhance the output information of the "-X" option by redefining the value of the $PS4 using some built-in variables. For example, execute the export ps4= ' +{$LINENO: ${funcname[0]} ' and then execute the script using the '-X ' option to display its line number and the name of the function it belongs to in front of each actually executed command.

The following is an example of a bug-based shell script, which this article will use to demonstrate how to debug a shell script with "-N" and enhanced "-X" execution options. This script defines a function isroot (), which is used to determine whether the current user is a root user, and if not, aborts the execution of the script

$ cat-n exp4.sh      1  #!/bin/bash      2  isRoot ()   & nbsp;  3  {     4          if ["$UID"-ne 0]      5                   return 1      6          else      7                   return 0      8          Fi      9 }     10  isRoot     11  if ["$?"-ne 0] &nbs p;   12  then     13          echo "Must Be root-to-run this script "    14          Exit 1     15  else     16     & nbsp;    echo "Welcome root user"     17           #do something     18  fi  

The Sh-n exp4.sh is executed first for the syntax check, and the output is as follows:

$ sh-n exp4.sh exp4.sh:line 6:syntax error near unexpected token ' Else ' exp4.sh:line 6: ' Else '

A syntax error was found, and by examining the command before and after the 6th line, we found that the IF statement in line 4th was missing the then keyword (the person who was accustomed to the C program was prone to make the mistake). We can change the 4th line to if ["$UID"-ne 0]; Then to fix this error. Run Sh-n exp4.sh again to check for syntax, and no more errors are reported. The next step is to actually execute the script, and the results are as follows:

$ sh exp4.sh exp2.sh:line one: [1:command not found welcome root user

Although the script does not have a syntax error, it reports an error when it executes. The error message is also very strange "[1:command not Found". Now we can try to customize the value of the $PS4 and use the "-X" option to track:

$ export ps4= ' +{$LINENO: ${funcname[0]}} ' $ sh-x exp4.sh +{10:} isRoot +{4:isroot} ' [' 503-ne 0 '] ' +{5:isroot} return 1 +{11:} ' [1 '-ne 0 '] ' exp4.sh:line: [1:command not found +{16:} echo ' Welcome root user ' Welcome root user

From the output, we can see that the script is actually executed statement, the line number of the statement and the name of the function is also printed out, from which you can clearly analyze the script execution trajectory and the function of the internal execution of the call. Since the execution is the 11th line error, this is an if statement, we compare and analyze the same as if statement of the 4th line of the trace results:

+{4:isroot} ' [' 503-ne 0 '] ' +{11:} ' [1 '-ne 0 '] '

It is known that the 11th line of the [number is missing a space, resulting in the [number and close to its variable $?] The value 1 is considered a whole by the shell interpreter, and try to treat this whole as a command to execute, so there is "[1:command not found" such as the error hint. Just insert a space after the [number] and everything is fine.

There are other built-in variables that are useful for debugging, such as Bash_source in the BASH shell, Bash_subshell, and so on, which are useful for debugging built-in variables that you can view with man sh or man BASH. These built-in variables are then used to customize the $PS4 for your debugging purposes, thus achieving the purpose of enhancing the output information of the "-X" option.

Five. Summary

Now let's summarize the process of debugging a shell script: First Use the "-N" option to check for syntax errors, and then use the "-X" option to track the execution of the script, and before using the "-X" option, don't forget to customize the value of the PS4 variable to enhance the output of the "-X" option, at least to output First execute export ps4= ' +[$LINENO] ', and once and for all, add this statement to your home directory's. bash_profile file, which will make your debugging journey easier. You can also use traps, debug hooks and other means to output key debugging information, quickly reduce the scope of troubleshooting errors, and in the script using "Set-x" and "set +x" to focus on some blocks of code tracking. This way, I believe you can easily catch the bug in your shell script. If your script is complex enough and requires more debugging, you can use the Shell debugger bashdb, a gdb-like debugging tool that can accomplish many of the features of a shell script, such as breakpoint setting, stepping, variable observation, etc. Using BASHDB can also be useful for reading and understanding complex shell scripts. The installation and use of BASHDB is outside the scope of this article, you can refer to the documentation on http://bashdb.sourceforge.net/and download the trial.

Shell Script Debugging Technology

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.