========================== CST8129 Term Assignment #1 ========================== -IAN! idallen@ncf.ca Due: 17:00 (5pm) Friday November 1, 2002 Marks: 2+2+3=7% Late penalty: 50% per day Purpose: - practice writing real shell scripts (material in Chapter 8 and 9) Hand in format: online submission only - no paper, no diskettes All scripts described below must be written to conform to the script writing checklist: script_checklist.txt and to the script style given in: script_style.txt. All user input (command line arguments or input via "read") must be fully validated before being used in expressions. Do not process bad input! Echo user input (including command line arguments given) back to the user. This is usually a good idea both for debugging your script and giving the user feedback on what data the script is actually processing. Avoid Linux-only commands and command options. The same script should work without modification on both Linux and ACADUNIX, where possible. (In particular, do *not* use Bash 2.x shell syntax!) Test your scripts. The sample inputs and output shown below are not a complete test suite. I will try to find test cases using glob patterns and blanks that will make your scripts abort or misbehave. Scripts without *useful* block comments will be severely penalized. ------------------ Hand in directory: ------------------ Completed scripts must have permissions "read-write-execute-only" for you, "read-only" permissions for group, no permissions for other people. The following directory is ready to receive your completed scripts: ~alleni/cst8129/02f/assignment01/xxxxnnnn/ where xxxxnnnn is your Algonquin userid (e.g. abcd0001). When you have completed a script, copy it into the above directory: cp myscript.sh ~alleni/cst8129/02f/assignment01/xxxxnnnn/myscript.sh Replace "myscript.sh" with the actual script name (given below). Files with the wrong name or wrong Unix permissions will be penalized. -------------------- Write these scripts: -------------------- --) Write this executable script named "20_integer_sorter.sh" (2 marks) This scripting is straightforward; the correct sorting algorithm is the item most students find difficult to get right. You probably need to write a correct pseudocode/PDL algorithm to do this first, before you write the script to do it. You might also verify your pseudocode with a small C program, first. Accept three or less arguments on the command line. If there are less than three arguments, prompt for and read the missing arguments (up to three). In other words: If the user supplies three command line arguments, don't prompt for any input; use the command line arguments. If the user supplies only two command line arguments, prompt for and read the missing third argument. If the user supplies only one command line argument, prompt for and read the missing two arguments. If the user supplies no command line arguments, prompt for and read all three missing arguments. See the optional_args_*.sh.txt scripts in the Notes for examples of how to do this. Echo the three input strings back to the user before using them. Make sure that the three inputs are all valid integers. See the test_numeric.sh.txt file in the Notes for a function that will do this for you. Using IF statements (not the sort command), arrange the three integers in order from smallest to largest. After the numbers have been put in order, output the numbers in this format: The numbers are, from smallest to largest: 23 156 9823 Exit with a zero return code if the numbers were already in sorted order. Exit with return code 1 if the numbers were not given in already sorted order. Exit with return code 2 on any errors (e.g. not an integer, too many arguments, etc.). Hints: The shortest solution is based on the "bubble sort" algorithm. Bubble the largest integer to the top of the list and then repeat for the remaining two numbers. You don't need a loop or array here, since there are only 3 numbers. A longer solution is possible using fully nested IF statements to handle all possible combinations of input; but, less code is better code. --) Write this executable script named "21_updown_series.sh" (2 marks) Accept three or less arguments on the command line. If there are less than three arguments, prompt for and read the missing arguments (up to three). Make sure that all three inputs are valid integers. Make sure that the third integer is not zero. We will call this third number the "increment". The first two numbers will be called the "range" numbers. Output all numbers between the two range numbers, using the third number as the increment. (You will need to write a PDL specification to get this algorithm right.) Example output (you must use better prompts in your own scripts): $ ./foo 1 3 1 You entered '1' '3' '1' 1 2 3 $ ./foo 1 3 -1 You entered '1' '3' '-1' 3 2 1 $ ./foo -5 -4 -10 You entered '-5' '-4' '-10' -4 $ ./foo -99 -99 1 You entered '-99' '-99' '1' -99 $ ./foo 1007 1000 2 You entered '1007' '1000' '2' 1000 1002 1004 1006 $ ./foo Input: 1007 1000 -2 You entered '1007' '1000' '-2' 1007 1005 1003 1001 Exit with status 0 if more than one number was printed. Exit with status 1 if only one number was printed. Exit with status 2 on any errors (e.g. not integers, too many arguments, etc.). Do not duplicate the loop code for every possible case! Set shell variables for (a) your loop start value, (b) loop end value, and (c) the test condition ("-le" or "-ge") and write the loop code only *once*, using the variables. For example, choose your loop test based on the sign of the increment: if [ $increment -lt 0 ] ; then looptest="-ge" else looptest="-le" fi (Purists will note that you are effectively writing self-modifying code if you make this work. I like to think of it as very thorough parameterization. Less code is better code.) You will note that you must count down if the increment is negative, and count up if the increment is positive. The first two numbers may entered in any order - your script must put them in the correct order to make the script work as shown. --) Write this executable script named "22_file_info.sh" (3 marks) This script will print information about each of the arguments found on the command line (if any). The script must print an error message about missing arguments and exit with a status of 2 if there are no command line arguments given. Prompt for and read two strings from the user. Verify that each string is a valid integer. We will call these integers the "range" integers. Make sure both range integers are non-negative. For each argument on the command line, treat the argument string as a Unix pathname and do the following tests: *) Initialize a "flag" variable to be the empty string. *) If the argument does not exist, append "??????" (six characters) to the flag variable and then do the final "Output" section, below. Skip all the following tests. *) If the argument is a file, append "F" (one letter) to the flag variable. *) If the argument is a directory, append "D" (one letter) to the flag variable. *) If the argument is neither file nor directory, append "-" (a single dash) to the flag variable. *) If the argument is a readable pathname, append "R" to the flag variable, otherwise append "-". *) If the argument is a writable pathname, append "W" to the flag variable, otherwise append "-". *) If the argument is an executable pathname, append "X" to the flag variable, otherwise append "-". *) If the argument is a file or a directory, find out the size in bytes of the file or directory and do this: *) If the number of bytes is zero, append " Z" (two characters - short for "zero size") to the flag variable. *) If the number of bytes is not zero but is below the smallest "range" integer, append " S" (two characters - short for "small") to the flag variable. *) If the number of bytes is equal to or between the "range" integers (inclusive), append " M" (two characters - short for "medium") to the flag variable. *) If the number of bytes is above the largest "range" integer, append " L" (two characters - short for "large") to the flag variable. *) If the argument is not a file or directory, append " ?" (two characters) to the flag variable instead of the size information. *) Output the flag variable and the file name on the same line. After all the command line arguments have been processed, print a count of how many items were processed, in the following order: - a count of missing pathnames - a count of files - a count of directories - a count of "other" pathnames Exit with return code 2 on any errors (including missing arguments). Exit with return code 0 if *all* the command line arguments existed. Exit with return code 1 if *any* command line argument was missing (did not exist). Note that the two range integers may be entered in any order. Your script must handle that and put them in the correct order to get the correct output. The script must always output six characters from the flag variable. Example output (you must use better prompts in your own scripts): $ ls -ld /bin /etc/passwd /etc/group /dev/null /etc/resolv.conf drwxr-xr-x 2 root root 4096 Aug 20 11:14 /bin crw-rw-rw- 1 root root 1, 3 Aug 30 2001 /dev/null -rw-r--r-- 1 root root 505 Aug 20 11:18 /etc/group -rw-r--r-- 1 root root 1139 Aug 20 11:18 /etc/passwd -rw-r--r-- 1 root root 53 Aug 28 15:33 /etc/resolv.conf $ ./foo /bin /etc/passwd /etc/group /dev/null /etc/resolv.conf /nosuch Input: 1000 100 You entered '1000' and '100' DR-X L /bin FR-- L /etc/passwd FR-- M /etc/group -RW- ? /dev/null FR-- S /etc/resolv.conf ?????? /nosuch Missing: 1 Files: 3 Directories: 1 Other: 1 Hints: To get the file/directory size in bytes, select one field in the output of one of "ls" or "stat". (Some options to stat will make the selection much easier. RTFM.) Do not use the "wc" command, since it cannot count bytes in directories or in files that are not readable. Selecting fields is explained in the data_mining.txt file under Notes. Structure your code correctly with IF/ELIF/ELSE statements to minimize the amount of testing you do. Write a small program! Try testing your code on directories containing lots of pathanames, e.g. /bin/*, /dev/*, /etc/*, /home/*, etc.