1. Trang chủ
  2. » Công Nghệ Thông Tin

Mastering Unix Shell Scripting phần 9 pps

71 217 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 71
Dung lượng 397,68 KB

Nội dung

545 Have you ever had a need to do some floating-point math in a shell script? If the answer is yes, then you’re in luck. On Unix machines there is a utility called bc that is an interpreter for arbitrary-precision arithmetic language. The bc command is an inter- active program that provides arbitrary-precision arithmetic. You can start an interac- tive bc session by typing bc on the command line. Once in the session you can enter most complex arithmetic expressions as you would in a calculator. The bc utility can handle more than I can cover in this chapter, so we are going to keep the scope limited to simple floating-point math in shell scripts. In this chapter we are going to create shell scripts that add, subtract, multiply, divide, and average a list of numbers. With each of these shell scripts the user has the option of specifying a scale, which is the number of significant digits to the right of the decimal point. If no scale is specified, then an integer value is given in the result. Because the bc utility is an interactive program, we are going to use a here document to supply input to the interactive bc program. We will cover using a here document in detail throughout this chapter. Syntax By now you know the routine: We need to know the syntax before we can create a shell script. Depending on what we are doing we need to create a mathematical statement to Floating-Point Math and the bc Utility CHAPTER 22 present to bc for a here document to work. A here document works kind of like a label in other programming languages. The syntax that we are going to use in this chapter will have the following form: VARIABLE=$(bc <<LABEL scale=$SCALE ($MATH_STATEMENT) LABEL) The way a here document works is some label name, in this case LABEL, is added just after the bc command. This LABEL has double redirection for input into the interactive program, bc <<LABEL. From this starting label until the same label is encountered again everything in between is used as input to the bc program. By doing this we are automating an interactive program. We can also do this automation using another technique. We can use echo, print, and printf to print all of the data for the math statement and pipe the output to bc. It works like the following commands. VARIABLE=$(print ‘scale = 10; 104348/33215’ | bc) or VARIABLE=$(print ‘scale=$SCALE; ($MATH_STATEMENT)’ | bc) In either case we are automating an interactive program. This is the purpose of a here document. It is called a here document because the required input is here, as opposed to somewhere else, such as user input from the keyboard. When all of the required input is supplied here, it is a here document. Creating Some Shell Scripts Using bc We have the basic syntax, so let’s start with a simple shell script to add numbers together. The script is expecting a list of numbers as command-line arguments. Addi- tionally, the user may specify a scale if the user wants the result calculated as a floating- point number to a set precision. If a floating point number is not specified, then the result is presented as an integer value. Creating the float_add.ksh Shell Script The first shell script that we are going to create is float_add.ksh. The idea of this shell script is to add a list of numbers together that the user provides as command-line arguments. The user also has the option of setting a scale for the precision of floating- point numbers. Let’s take a look at the float_add.ksh shell script in Listing 22.1, and we will go through the details at the end. 546 Chapter 22 #!/usr/bin/ksh # # SCRIPT: float_add.ksh # AUTHOR: Randy Michael # DATE: 03/01/2001 # REV: 1.1.A # # PURPOSE: This shell script is used to add a list of numbers # together. The numbers can be either integers or floating- # point numbers. For floating-point numbers the user has # the option of specifying a scale of the number of digits to # the right of the decimal point. The scale is set by adding # a -s or -S followed by an integer number. # # EXIT CODES: # 0 ==> This script completed without error # 1 ==> Usage error # 2 ==> This script exited on a trapped signal # # REV. LIST: # # # set -x # Uncomment to debug this script # set -n # Uncomment to debug without any command execution # ######################################################## ############## DEFINE VARIABLE HERE #################### ######################################################## SCRIPT_NAME=$(basename $0) # The name of this shell script SCALE=”0” # Initialize the scale value to zero NUM_LIST= # Initialize the NUM_LIST variable to NULL COUNT=0 # Initialize the counter to zero MAX_COUNT=$# # Set MAX_COUNT to the total number of # command-line arguments. ######################################################## ################ FUNCTIONS ############################# ######################################################## function usage { echo “\nPURPOSE: Adds a list of numbers together\n” echo “USAGE: $SCRIPT_NAME [-s scale_value] N1 N2 Nn” Listing 22.1 float_add.ksh shell script listing. (continues) Floating-Point Math and the bc Utility 547 echo “\nFor an integer result without any significant decimal places ” echo “\nEXAMPLE: $SCRIPT_NAME 2048.221 65536 \n” echo “OR for 4 significant decimal places” echo “\nEXAMPLE: $SCRIPT_NAME -s 4 8.09838 2048 65536 42.632” echo “\n\t EXITING \n” } ######################################################## function exit_trap { echo “\n EXITING on trapped signal \n” } ######################################################## ################# START OF MAIN ######################## ######################################################## ###### Set a Trap ###### trap ‘exit_trap; exit 2’ 1 2 3 15 ######################################################## # Check for at least two command-line arguments if [ $# -lt 2 ] then echo “\nERROR: Please provide a list of numbers to add” usage exit 1 fi # Parse the command-line arguments to find the scale value, if present. while getopts “:s:S:” ARGUMENT do case $ARGUMENT in s|S) SCALE=$OPTARG ;; \?) # Because we may have negative numbers we need # to test to see if the ARGUMENT that begins with a # hyphen (-) is a number, and not an invalid switch!!! for TST_ARG in $* do if [[ $(echo $TST_ARG | cut -c1) = ‘-’ ]] \ Listing 22.1 float_add.ksh shell script listing. (continued) 548 Chapter 22 && [ $TST_ARG != ‘-s’ -a $TST_ARG != ‘-S’ ] then case $TST_ARG in +([-0-9])) : # No-op, do nothing ;; +([-0-9].[0-9])) : # No-op, do nothing ;; +([ 0-9])) : # No-op, do nothing ;; *) echo “\nERROR: Invalid argument on the command line” usage exit 1 ;; esac fi done ;; esac done ######################################################## # Parse through the command-line arguments and gather a list # of numbers to add together and test each value. while ((COUNT < MAX_COUNT)) do ((COUNT = COUNT + 1)) TOKEN=$1 # Grab a command line argument on each loop iteration case $TOKEN in # Test each value and look for a scale value. -s|-S) shift 2 ((COUNT = COUNT + 1)) ;; -s${SCALE}) shift ;; -S${SCALE}) shift ;; *) # Add the number ($TOKEN) to the list NUM_LIST=”${NUM_LIST} $TOKEN” ((COUNT < MAX_COUNT)) && shift ;; esac done ######################################################## Listing 22.1 float_add.ksh shell script listing. (continues) Floating-Point Math and the bc Utility 549 # Ensure that the scale is an integer value case $SCALE in # Test for an integer +([0-9])) : # No-Op - Do Nothing ;; *) echo “\nERROR: Invalid scale - $SCALE - Must be an integer” usage exit 1 ;; esac ######################################################## # Check each number supplied to ensure that the “numbers” # are either integers or floating-point numbers. for NUM in $NUM_LIST do case $NUM in +([0-9])) # Check for an integer : # No-op, do nothing. ;; +([-0-9])) # Check for a negative whole number : # No-op, do nothing ;; +([0-9]|[.][0-9])) # Check for a positive floating point number : # No-op, do nothing ;; +(+[0-9][.][0-9])) # Check for a positive floating point number # with a + prefix : # No-op, do nothing ;; +(-[0-9][.][0-9])) # Check for a negative floating point number : # No-op, do nothing ;; +([ 0-9])) # Check for a negative floating point number : # No-op, do nothing ;; +([+.0-9])) # Check for a positive floating point number Listing 22.1 float_add.ksh shell script listing. (continued) 550 Chapter 22 : # No-op, do nothing ;; *) echo “\nERROR: $NUM is NOT a valid number” usage exit 1 ;; esac done ######################################################## # Build the list of numbers to add ADD= # Initialize the ADD variable to NULL PLUS= # Initialize the PLUS variable to NULL # Loop through each number and build a math statement that # will add all of the numbers together. for X in $NUM_LIST do # If the number has a + prefix, remove it! if [[ $(echo $X | cut -c1) = ‘+’ ]] then X=$(echo $X | cut -c2-) fi ADD=”$ADD $PLUS $X” PLUS=”+” done ######################################################## # Do the math here by using a here document to supply # input to the bc command. The sum of the numbers is # assigned to the SUM variable. SUM=$(bc <<EOF scale = $SCALE (${ADD}) EOF) ######################################################## # Present the result of the addition to the user. echo “\nThe sum of: $ADD” echo “\nis: ${SUM}\n” Listing 22.1 float_add.ksh shell script listing. (continued) Floating-Point Math and the bc Utility 551 Let’s take it from the top. We start the shell script in Listing 22.1 by defining some variables. These five variables, SCRIPT_NAME, SCALE, NUM_LIST, COUNT, and MAX_COUNT are predefined for later use. The SCRIPT_NAME variable assignment extracts the filename of the script from the system using the basename $0 command, and SCALE is used to define the precision of floating-point numbers that are calcu- lated. The NUM_LIST variable is used to hold valid numbers that are to be calculated, where the command switch and the switch-argument are removed from the list. The COUNT and MAX_COUNT variables are used to scan all of the command-line arguments to find the numbers In the next section we define the functions. This shell script has two functions, usage and exit_trap. The usage function shows the user how to use the script, and the exit_trap function is executed only when a trapped exit signal is captured. Of course, you cannot trap a kill -9. At the START OF MAIN the first thing that we do is to set a trap. A trap allows us to take some action when the trapped signal is captured. For example, if the user presses CTRL-C we may want to clean up some temporary files before the script exits. A trap allows us to do this. A trap has the form of trap '{command; command; ; exit 2' 1 2 3 15. We first enclose the commands that we want to execute within tic marks (single quotes) and then give a list of exit signals that we want to capture. As I said before, it is not possible to cap- ture a kill -9 signal because the system really just yanks the process out of the process table and it ceases to exist. After setting the trap we move on to verifying that each of the command-line argu- ments is valid. To do this verification we do five tests. These five tests consist of check- ing for at least two command-line arguments, using getopts to parse the command-line switches, test for invalid switches, and assign switch-arguments to variables for use in the shell script. The next step is to scan each argument on the command line and extract the numbers that we need to do our calculations. Then the $SCALE value is checked to ensure that it points to an integer value, and the final test is to check the “numbers” that we gathered from the command-line scan and ensure that each one is either an integer or a floating-point number. Testing for Integers and Floating-Point Numbers I want to go over the integer and floating-point test before we move on. At this point in the script we have a list of “numbers”—at least they are supposed to be numbers—and this list is assigned to the NUM_LIST variable. Our job is to verify that each value in the list is either an integer or a floating-pointing number. Look at the code segment shown in Listing 22.2. # Check each number supplied to ensure that the “numbers” # are either integers or floating-point numbers. for NUM in $NUM_LIST do case $NUM in Listing 22.2 Testing for integers and floating-point numbers. 552 Chapter 22 +([0-9])) # Check for an integer : # No-op, do nothing. ;; +(-[0-9])) # Check for a negative whole number : # No-op, do nothing ;; +([0-9]|[.][0-9])) # Check for a positive floating point number : # No-op, do nothing ;; +(+[0-9]|[.][0-9])) # Check for a positive floating point number # with a + prefix : # No-op, do nothing ;; +(-[0-9][.][0-9])) # Check for a negative floating point number : # No-op, do nothing ;; +([ 0-9])) # Check for a negative floating point number : # No-op, do nothing ;; +([+.0-9])) # Check for a positive floating point number : # No-op, do nothing ;; *) echo “\nERROR: $NUM is NOT a valid number” usage exit 1 ;; esac done Listing 22.2 Testing for integers and floating-point numbers. (continued) We use a for loop to test each value in the NUM_LIST. On each loop iteration the cur- rent value in the $NUM_LIST is assigned to the NUM variable. Within the for loop we have set up a case statement. For the tests we use regular expressions to indicate a range, or type of value, that we are expecting. If the value does not meet the criteria that we defined, the * is matched and we execute the usage function before exiting the shell script. The regular expressions for testing for integers and floating point numbers include +([0-9]), +(-[0-9]), +([0-9]|.[0-9], +(+[0-9].[0-9], +(-[0-9].[0-9], +([ 0-9]), +([+.0-9]). The first two tests are for integers and negative whole numbers. The last five tests are for positive and negative floating point numbers. Floating-Point Math and the bc Utility 553 Notice the use of the plus sign (+), minus sign (-), and the decimal point (.). The place- ment of the plus sign, minus sign, and the decimal point are important when testing the string. Because a floating point number, both positive and negative, can be repre- sented in many forms we need to test for all combinations. Floating point numbers are one of the more difficult tests to make as you can see by the number of tests that are required. Building a Math Statement for the bc Command Once we are sure that all of the data is valid we proceed to building the actual math statement that we are sending to the bc utility. To build this statement we are going to loop through our newly confirmed $NUM_LIST of numbers and build a string with a plus sign (+) between each of the numbers in the $NUM_LIST. This is a neat trick. We first initialize two variables to NULL, as shown here. ADD= PLUS= As we build the math statement, the ADD variable will hold the entire statement as it is added to. The PLUS variable will be assigned the + character inside of the for loop on the first loop iteration. This action prevents the + sign showing up as the first character in the string we are building. Let’s look at this code segment here. ADD= # Initialize the ADD variable to NULL PLUS= # Initialize the PLUS variable to NULL # Loop through each number and build a math statement that # will add all of the numbers together. for X in $NUM_LIST do if [[ $(echo $X | cut -c1) = ‘+’ ]] then X=$(echo $X | cut -c2-) fi ADD=”$ADD $PLUS $X” PLUS=”+” done On the first loop iteration only the first number in the $NUM_LIST is assigned to the ADD variable. On each of the following loop iterations a plus sign (+) is added followed by the next number in the $NUM_LIST, specified by the X variable on each loop itera- tion, until all of the numbers and plus signs have been assigned to the ADD variable. As an example, we have the following list of numbers: 12 453.766 223.6 3.145927 22 Also notice that we added a test for the number beginning with a + sign. We need to strip this character out so that we do not have two plus signs together when we present 554 Chapter 22 [...]... case $NUM in +([0 -9] )) # Check for an integer : # No-op, do nothing ;; +([-0 -9] )) # Check for a negative whole number : # No-op, do nothing ;; +([0 -9] |[.][0 -9] )) # Check for a positive floating point number : # No-op, do nothing ;; +(+[0 -9] |[.][0 -9] )) # Check for a positive floating point number # with a + prefix : # No-op, do nothing ;; +([-0 -9] |.[0 -9] )) Listing 22.4 float_subtract.ksh shell script listing... +([0 -9] )) # Check for an integer : # No-op, do nothing ;; +([-0 -9] )) # Check for a negative whole number : # No-op, do nothing ;; +([0 -9] |[.][0 -9] )) # Check for a positive floating point : # No-op, do nothing ;; +(+[0 -9] |[.][0 -9] )) # Check for a positive floating point # with a + prefix : # No-op, do nothing ;; +([-0 -9] |.[0 -9] )) # Check for a negative floating point : # No-op, do nothing ;; +(-.[0 -9] ))... Listing 22.6 float_multiply.ksh shell script listing (continued) Floating-Point Math and the bc Utility ;; +([-0 -9] )) # Check for a negative whole number : # No-op, do nothing ;; +([0 -9] |[.][0 -9] )) # Check for a positive floating point : # No-op, do nothing ;; +(+[0 -9] |[.][0 -9] )) # Check for a positive floating point # with a + prefix : # No-op, do nothing ;; +([-0 -9] |.[0 -9] )) # Check for a negative floating... integer : # No-op, do nothing ;; +([-0 -9] )) # Check for a negative whole number : # No-op, do nothing ;; +([0 -9] |[.][0 -9] )) # Check for a positive floating point : # No-op, do nothing ;; +(+[0 -9] |[.][0 -9] )) # Check for a positive floating point # with a + prefix : # No-op, do nothing ;; +([-0 -9] |.[0 -9] )) # Check for a negative floating point : # No-op, do nothing ;; +(-.[0 -9] )) # Check for a negative floating... float_divide.ksh shell script in action [root:yogi]@/scripts# float_divide.ksh -s 6 3321 -332.8 89 The quotient of: 3321 / -332.8 89 to a scale of 6 is -.00 099 7 Listing 22.11 float_divide.ksh shell script in action Notice that the scale worked with the division script! We have completed shell scripts for addition, subtraction, multiplication, and division I want to present one more variation in the next section 5 79. .. testing, or using, a shell script with a here document To be safe, just leave out any beginning spaces The final step is to display the result to the user Listing 22.3 shows the float_add.ksh shell script in action [root:yogi]@/scripts# /float_add.ksh -s 8 2 223.545 332.0 099 76553 The sum of: 2 + 223.545 + 332.0 099 76553 to a scale of 8 is 557.55 497 6553 Listing 22.3 float_add.ksh shell script in action... -c1) = ‘-’ ]] \ && [ $TST_ARG != ‘-s’ -a $TST_ARG != ‘-S’ ] then case $TST_ARG in +([-0 -9] )) : # No-op, do nothing ;; +([-0 -9] .[0 -9] )) : # No-op, do nothing ;; +([-.0 -9] )) : # No-op, do nothing ;; *) echo “\nERROR: $TST_ARG is an invalid argument\n” usage exit 1 ;; esac fi done ;; esac Listing 22.6 float_multiply.ksh shell script listing (continues) 567 568 Chapter 22 done ########################################################... Creating the float_subtract.ksh Shell Script As the float_add.ksh shell script performed addition on a series of numbers, this section studies the technique of subtraction Because this shell script is very similar to the shell script in Listing 22.1 we are going to show the shell script and study the details at the end The float_subtract.ksh shell script is shown in Listing 22.4 #!/usr/bin/ksh # # SCRIPT:... float_multiply.ksh shell script shown in Listing 22.6 and study the subtle changes from the previous two shell scripts in Listing 22.1 and Listing 22.3 The float_multiply.ksh shell script is shown in action in Listing 22.8 Notice in this output that the scale setting still has no effect on the output [root:yogi]@/scripts# float_multiply.ksh -s 4 The product of: 8. 098 38 2048 65536 42.632 8. 098 38 * 2048 *... $TST_ARG | cut -c1) = ‘-’ ]] \ && [ $TST_ARG != ‘-s’ -a $TST_ARG != ‘-S’ ] then case $TST_ARG in +([-0 -9] )) : # No-op, do nothing ;; +([-0 -9] .[0 -9] )) : # No-op, do nothing ;; +([-.0 -9] )) : # No-op, do nothing ;; *) echo “\nERROR: $TST_ARG is an invalid argument\n” usage Listing 22.10 float_divide.ksh shell script listing (continued) Floating-Point Math and the bc Utility exit 1 ;; esac fi done ;; esac . the shell script. The regular expressions for testing for integers and floating point numbers include +([0 -9] ), +(-[0 -9] ), +([0 -9] |.[0 -9] , +(+[0 -9] .[0 -9] , +(-[0 -9] .[0 -9] , +([ 0 -9] ), +([+.0 -9] ) [root:yogi]@/scripts# ./float_add.ksh -s 8 2 223.545 332.0 099 76553 The sum of: 2 + 223.545 + 332.0 099 76553 to a scale of 8 is 557.55 497 6553 Listing 22.3 float_add.ksh shell script in action. Floating-Point Math. do nothing ;; +(+[0 -9] |[.][0 -9] )) # Check for a positive floating point number # with a + prefix : # No-op, do nothing ;; +([-0 -9] |.[0 -9] )) Listing 22.4 float_subtract.ksh shell script listing.

Ngày đăng: 14/08/2014, 16:20