KORN SHELL SCRIPTS, #1-38 SCRIPT 1 #!/bin/ksh #Simple Output. #KSH support three output statements: echo and print. #Since we,ve already covered echo, we'll usr print and printf here. #USAGE statement demonstrate how to start script USAGE="usage: 1_output.ksh" echo "Hello world." #old command print #prints a blank line #Use print to print output text. Although you don't have #to surround you text in single or double quotes, it's a #good idea because quotes will preserve all white space #within your output. print Hi earth. #okay print "Hi earth." #better #By default, the print and echo end by a newline. If you #don't want a new line use the -n option. print -n "Bon" #-n inhibits the newline print -n "jour " #here also print "le monde." #no -n; therefore, newline #The \n is an explicit order to print a newline character. print "hi\nto\nyou" #prints a single word on each line #Use \t within your text to print a tab print "\tBuenos dias al mundo." #printf is for formatted printing. Its syntax is printf "format", [arguments]+ #Format portion supports printing of a single character (%c), strings (%s), integers (%d) and #floating point (%f). Stings and integers can be right (+) or left (-) justified. Numbers are #backfilled by leading 0s. Their is also a min.max field range available. printf "%d\n", 123 #default is left justified, 10 characters max printf "%04d\n", 123 #4 field positions, 0 fill printf "%2d\n", 1234567890 #prints all 10 numbers despite 2. Overflow is automatic. printf "%8.4s\n", abc #8 character field, print 4 characters, right justified. printf "%8.4s\n", abcde #8 character field, print 4 characters, e is truncated. printf "%2.4s\n", abcde #despite 2 character field, prints 4 characters, considered an error. SCRIPT 2 #!/bin/ksh USAGE="usage: $0" #from now on make this part of the standard in script writing. #Printing the Value of a variable print -n "Enter a number, letter, word, etc.: " read x #assigns x to whatever user enters up to one line print #blank line #Contrast the following three print statements print "x" #prints the letter x print "$x" #prints the value of variable x print "x = $x" #statement printing #This read statement gathers one line of input and assigns it to 3 variables print -n "Enter three numbers: " read first second third #If you entered a 10 20 30, then first equals 10, second equals 20 and third equals 30 print "first variable is $first." print "second variable is $second." print "third variable is $third." #Here's 4 ways to assign a numerical value to a variable n=100 #Most prefer this syntax, remember no spaces let n=100 let "n = 100" ((n = 100)) print "The value of n is $n" #You can assign a letter, word, phrase to a variable by #specifiying the variable name, and equal sign, and the #value: for example: letter="Q" word="elephant" phrase="The rain in Texas..." print "letter = $letter; word = $word; phrase = $phrase" #To assign the value of one variable to another use equal #sign y=$n #assigns the value of variable n to y print "y = $y" SCRIPT 3 #If your Korn shell script isn't working properly, there are two ways #to figure out what went wrong. #Method 1: You can stare intently at the terminal until large vats of # sweat pour down your brow. Not preferred #Method 2: You can run the script in debug mode. (preferred!!) #To run the entire script in debug mode, invoke like this: #1. Take out ksh entry in top of script. #2. start the script by typing: ksh -x script_name #The plus signs you get are the default debug mode indicator, this traces #the execution of your script. The lines not proceded by a + are the script's #input and output. #A script running in debug mode is very slow and produces a large amount of #output. So now I introduce you to a even smarter way to debug scripts. #You can run just regions of the script in debug and the rest normally. #To start a region in debug mode, in the script itself at the top of the #region place the directive set -x and at the end of the region a set +x. #This can be used in many areas of the script at one time. Remember set -x #turn it on and a set +x terminates debugger (It may seems backwards). SCRIPT 4 #!/bin/ksh #The above line must always be in line 1, column 1, else all it is to the script is a comment. #This forces the shell to be Korn. USAGE="$0" #The above is nothing more than a string assignment. The variable USAGE is nothing to #the script unless accessed by echo, print or printf. If you grep for it in the script it #should tell you how to execute the script and what options and arguments to use. This one just #says to type in the script name which is $0. #DATA TYPES #DECLARING STRINGS #By default, every variable is a string, unless you tell the KornShell #otherwise. In the following example, KornShell automatically considers #each of the following a string. #Assign a value to 5 different string variables #can use typeset value...by default variables are strings letter="A" sentiment="always" book="UNIX for programmers and users" numerical_string="987" phone_message="Call me at 972-205-4555" print "Here are some string values: " print "$letter; $sentiment; $boot " print "$numerical_string, and $phone_message" #For clarity, you can explicitly declare a variable as a string typeset st st=$book print "$st" #DECLARING INTEGERS #You can declare an integer variable in two methods: #typeset -i variable_Name #integer variable_Name integer y y=100 print "y = $y" #You may also initialize an integer when declaring integer z=100 #Now that y is an integer, a string cannot be assigned to it. #y="My little Pony" #will cause an error, uncomment to see #You can still put an integer into a string x=150 #x is a string unless declared otherwise print "x = $x" x="Cats and dogs are our pets." #Stiil okay, remember a string print "x = $x" #DECLARING CONSTANTS #You declare constants with the -r (readonly) option of the typeset statement #or with the readonly (Bourne Shell) statement. Once declared, it can not be changed. #Declare legs_per_spider as a constant. typeset -r legs_per_spider=8 print "legs_per_spider = $legs_per_spider" #Declare string constant typeset -r string_constant="Nebraska is first in football." print "string_constant = $string_constant" #It is illegal to change the constant in the script once declared. #Try anyway: string_constant="This class is okay" #you should get an error SCRIPT 5 #!/bin/ksh USAGE="$0" #Korn shell supports one-dimensional arrays of integers or strings. You #can not explicitly declare a variable as an array. Instead this shell #creates an array the first time you assign a value to it. Each array #automatically holds up to 1024 values (Some older versions can only handle #up to 512 values). The first cell in an array is numbered 0. So the #array cells are numbered from 0 to 1023. By default all arrays are of #strings. If you however declare a variable as an integer and then use it as #an array, then you've created an array of integers. #Create an array of strings and assign them values animal[0]="mule" animal[1]="horse" animal[2]="cow" #You don't need to assign each cell a value in the array animal[5]="turkey" animal[12]="goat" #cells 3-4 and 6-11 contain the "NULL" value. #Create an array of integers and assign values to it integer test_scores #Step 1 test_scores[0]=100 #Step 2 test_scores[1]=95 test_scores[2]=90 print -n "Enter another test score: " read test_scores[3] #user can assign a cell in array #Below will not work.... #test_scores[4]="cat" #Illegal entry to an array of integers #An alternative way to assign values to an array is to use the set -A #statement (if supported by your version). #The following creates an array of strings names flowers and assigns #values to it: set -A flowers gardenia "bird of paradise" hibiscus #Printing an array is a little different print "Cell #0 contains ${flowers[0]}" print "Cell #2 contains ${flowers[1]}" cell_number=2 print "Cell $cell_number contains ${flowers[$cell_number]}" print #To print all the elements of an array: print "The entire array contains: ${flowers[*]}" #or like this: print "The entire array contains: ${flowers[@]}" #The differences will be explained later. #Beware of common array mistakes. The pair of braces confuses a lot of #array users. General rules: #1. Don't use the braces when the name of the array appears on the left side # of the assignment operator ( = ); example: # array[2]="rose" #not ${array[2]="rose" #2. You must use the braces when the name of the array appears on the right # side of the assignment operator; example: # flower=${array[2]} #3. When putting both sides together; the following statement assigns one # element of an array to another. # array[3]=${array[2]} #4. You should also use the braces when printing an array element: # print "${array[2]}" SCRIPT 6 #!/bin/ksh USAGE="$0" #List of all mathematical operators. #OPERATION OPERATOR EXAMPLE RESULT #Addition + ((y = 7 + 10)) 17 #Subtraction - ((y = 10 - 3)) 7 #Multiplication * ((y = 5 * 6)) 30 #Division / ((y = 33 / 4)) 8 #Modulo Div. % ((y = 33 % 4)) 1 #Bit Left shift << ((y = 2#1011 << 2)) 2#101100 #Bit Right shift >> ((y = 1011 >> 2)) 2#0010 #Bitwise AND & ((y = 2#1010 & 2#1100)) 2#1000 #Bitwise exc OR ^ ((y = 2#1010 ^ 2#1100)) 2#0110 #Bitwise OR | ((y = 2#1010 | 2#1100)) 2#1110 #You should perform math operations inside a pair of double parenthesis. #You can place any amount of white space inside the parenthesis integer x=5 integer y=7 integer z ((z = x + y)) print "$x plus $y is $z" #To confuse the issue, you can also perform operations on string variables, #meaning that you don't have to declare a variable as an integer to use in #a math problem. print -n "How many birds are on the fence? " read birds print -n "How many cats live around the area? " read num_cats ((total_animals = birds + num_cats)) print "There are $total_animals cats and birds in the area." #You can perform math operations inside the print statement too print "There are $((birds + num_cats)) cats and birds in the area." #The dollar sign ($) is optional within the ((...)) operations #However; ((y = y + 50)) #is slower than ((y = $y +50)) #because with the first you are doing a straight integer instruction and #with the second the shell would have to converted the value of y to a #string, do the math add and then converted back to an integer. I guess #the whole things boils down to...avoid the dollar sign within ((...)) operations. #Grouping Math Operations- same as with all math functions, just make sure #that all in encased by the double ((...)) #((centigrade = fahrenheit - 32 * 5 / 9 )) Wrong answer achieved #((centigrade = ((fahrenheit - 32 ) * 5) / 9)) Right answer achieved #The KornShell has a simplistic view of division. Each division answer has two #parts: the Kornshell provides two division operators: one for the quotient (/) and #the (%) operator called the "mudulo division operator". #Example: print -n "How far did you run (in meters)? " read distance ((kilometers = distance / 1000)) ((remaining_meters = distance % 1000)) print "You ran $kilometers kilometers and $remaining_meters meters." SCRIPT 7 #!/bin/ksh USAGE="$0" #This script demonstrates how to specify integers in various bases. #By default all input and output are base 10 (decimal) integers. #You can specify any base from 2 to 36 inclusive typeset -i x #declares x as a base 10 integer typeset -i2 y #declares y as a base 2 (binary) integer typeset -i8 z #declares z as a base 8 (octal) integer typeset -i16 h #declares h as a base 16 (hex) integer #Here we can convert an integer value between bases print -n "Enter an integer: " read x h=z=y=x #assign the value of x to y,z, and h #Kornshell outputs nondefault bases with the syntax: base#number #The # sign in the output is not a comment in this context. In fact, #the # sign is never a comment when preceded by a number print "Tranlated into base 2 $x is $y" print "Tranlated into base 8 $x is $z" print "Tranlated into base 16 $x is $h" #Two methods of inputting different bases from the command line or with read statements: # Method 1. # typeset -i16 hexnum # print -n "Enter a hex value: " # read hexnum # you would type 16#ABC012...Must include 16# as input! # #Method 2. # typeset -i16 hexnum # print -n "Enter a hex value: " # read raw_string # you would type ABC012...No 16# as input! # hexnum=16#$raw_string # print $hexnum SCRIPT 8 #!/bin/ksh USAGE="Usage:$0" #Outputting a Certain Number of digits #You can use the syntax: typeset -Zn to specify the number of digits for print #to output. Can only use on strings, not on integers. typeset -Z5 salary #Five characters print -n "Enter a salary: " read salary #a string not a number!! print "salary = $salary" #Example: 32500 would print 32500 # 7154 would print 07154 # 123456 would print 23456 SCRIPT 9 #!/bin/ksh USAGE="$0" #The Korn shell provides six different kinds of numerical comparisons and #eight different kinds of string comparisons. The comparisons are also #known as tests. #Place numerical tests inside a pair of double parenthesis (( . . . )) and #place string tests inside a pair of double square brackets [[ . . . ]]. #Difference between a numerical and string test: A numerical test compares two #numerical values and a string test compares two string values. If you specify #$variable inside a string test, then variable cannot have been declared as #an integer data type. #Within ((...)) you can use as much or as little white space as you desire and #you don't need to place dollar signs in front of variable names, but #with [[...]] you must specify white space between every component. #Numerical Tests Returns True if... #((number1 == number2)) number1 equals number2 #((number1 != number2)) number1 does not equal number2 #((number1 < number2)) number1 is less than number2 #((number1 > number2)) number1 is greater than number2 #((number1 <= number2)) number1 is less than or equal to number2 #((number1 >= number2)) number1 is greater than or equal to number2 #String Tests Returns True if... #[[ str1 = str2 ]] str1 equals str2 #[[ str1 = pattern ]] str1 matches pattern #[[ str1 != str2 ]] str1 does not equal str2 #[[ str1 != pattern ]] str1 does not equal pattern #[[ str1 < str2 ]] str1 precedes str2 in lexical order #[[ str1 > str2 ]] str1 follows str2 in lexical order #[[ -z str1 ]] str1's length is zero (null) #[[ -n str1 ]] str1's length is nonzero #Example: print -n "Enter two numbers: " read x y if ((x == y)) #use this syntax...Preferred then print "You entered the same number twice." fi if test $x -eq $y #Syntax 2: old Bourne shell versions then print "You entered the same number twice." fi if let "$x == $y" #Syntax 3 then print "You entered the same number twice." fi if [ $x -eq $y ] #Syntax 4 then print "You entered the same number twice." fi if [[ $x -eq $y ]] #Syntax 5 then print "You entered the same number twice." fi #Use the first syntax, it is considered the new proper one, the others #are included in case you encounter them in older Korn and Bourne scripts, #they won't look too alien to you. SCRIPT 10 #!/bin/ksh USAGE="Usage:$0" #The following script demonstrates how to assiociate multiple commands with a #condition. Unlike some high-level languages (like C or Pascal), you do not #mark the boundries of such compound statements with BEGIN,END or {} pairs. print -n "Enter a year: " read year #Associating one command with a condition: if (( (year % 2) == 0 )) then print "$year is even." fi #Associating multiple commands with a condition: if (( (year % 4) == 0 )) then print "$year is divisible by four." print "Perhaps it is a leap year." print "Perhaps it is a Olympic year." fi #Associating zero commands with a condition #The colon : is called the null statement. In the following context, #the null statement acts as a placeholder if (( (year % 47) == 0) then : #Code to be added later fi #If we had omitted the colon and left it blank, an error msg is issued print -n "Enter two integers: " read n1 n2 #Only one test if ((n1 < 0)) #the test then print "The first integer is negative. " else print "The first integer is non-negative." fi #multiple tests if ((n2 < 0)) then print "The second integer is negative." elif ((n2 == 0)) then print "The second integer is zero." else print "The second integer is positive." fi #Comparing strings with if print -n "Enter string1: " read string1 print -n "Enter string2: " read string2 print if [[ $string1 = $string2 ]] #Syntax 1: preferred then print "The two strings are identical using [[...]]." fi if [ "$string1" = "$string2" ] #Syntax 2 then print "The two strings are identical using [...]." fi if test "$string1" = "$string2" #Syntax 3 then print "The two strings are identical using test." fi SCRIPT 11 #!/bin/ksh USAGE="Usage:$0" #The < and > operators perform triple duty in the KornShell. #If you specify < or > within ((. . .)) the KornShell does a numerical #comparison. #If you specify < or > within [[ . . .]] the KornShell does a lexical comparison #of strings. #The third use of < or > is input and output redirection. #Comparing Alphabetical order with < and > print -n "Enter a string: " read s1 print -n "Enter another string: " read s2 #Compare the lexical order of strings if [[ $s1 < $s2 ]] then print "$s1 would appear before $s2 in the dictionary." elif [[ $s1 = $s2 ]] then print "Both strings are identical." else print "$s2 would appear before $s1 in the dictionary." fi #Comparing a string to a Pattern with if print -n "Enter a string: " read s if [[ $s = c* ]] #true if $s starts with a c then print "$s starts with the letter c." fi if [[ $s != *n ]] #true if $s does not end with an n then print "$s does not end with the letter n." fi #The following condition will be true if $s is one of the listed #fruits if [[ $s = @(orange|lemon|lime|grape|apple) ]] then print "$s is a common fruit." else print "$s is not a common fruit." fi SCRIPT 12 #!/bin/ksh USAGE="Usage:$0" #demonstrates && and || #Boolean AND operator && and the Boolean OR operator ||. integer age #declare age as an integer for read below print -n "Enter your age -- " read age if ((age < 6)) || ((age > 64)) # || is boolean OR then print "We give a discount to children and seniors." print "Tickets are \$2.50" elif ((age >= 13)) && ((age <= 19)) # && is boolean AND then print "You are a teenager. We charge double for teens." print "Tickets are \$10.00" elif ((age >= 40 && age <= 50)) then print "These are very difficult years. No charge." else print "Tickets are \$5.00" fi SCRIPT 13 #!/bin/ksh USAGE="Usage=$0" #Simple case statement. print -n "How do you feel? (wonderful, ok , not good) -- " read feeling #If feeling is any match other than what is below #then goes to default case $feeling in "wonderful"|"great"|"superb") print "I'm glad for you." print "Really glad." mood_quotient=10;; #break is two ;; "ok"|"good"|"fine") print "That's good." mood_quotient=5;; "not good"|"bad"|"rotten") print "I'm sorry to hear that." mood_quotient=1;; *) print "I wasn't expecting that answer." mood_quotient="undefined";; esac #end of case print "Your mood quotient was $mood_quotient." SCRIPT 14 #!/bin/ksh USAGE="$0" #Tests on Objects #It has become fashionable to speak of files, directories, links and other nouns #of computer science as objects. The KornShell provides an easy way to #determine #an objects characteristics. Place these tests inside a pair of double square #brackets # (and remember to leave some white space after [[ and before ]]); for example: #[[ -f $myfile ]] #is $myfile a regular ascii file? #[[ -x /home/usrid/assign6 ]] #is this file executable? #Each test returns a true or false. If you are testing only one object and that #one object #does not exist, then the test always returns false #KornShell Tests: # TEST RETURNS TRUE IF OBJECT: #-a object exists #-f object is a regular file #-d object is a directory #-c object is a special character file #-b object is a block special file #-p object is a pipe #-S object is a socket #-L object is a symbolic (soft) link to another object #-k object object's "sticky bit" is set #-s object object isn't empty (has a length of zero bytes) #Example 1: print -n "Enter the pathname of n object: " read pathname #The ! operator means "not". if [[ ! -a $pathname ]] #Does this object not exist? then print "There is no object at that pathname. " elif [[ -L $pathname ]] #Is it a Symbolic Link? then print "$pathname is a symbolic link." elif [[ -f $pathname ]] #Is it a regular file? then print "$pathname is a file." elif [[ -d $pathname ]] #Is it a directory? then print "$pathname is a directory." else #Object is therefore offbeat, like a socket or pipe print "$pathname is not a file, directory, or symbolic link." fi #As covered earlier in the class the owner of a object can specify #read, write and execute permissions and can use the chmod utility #to alter these permissions. #Object permissions tests: #TEST RETURNS TRUE IF: #-r object I may read this object #-w object I may write to (modify) this object #-x object Object is a file, then I may execute it; if not then if it is a directory, search through it #-O object I own object #-G object The group to which I belong owns object #-u object object's set-user-id is set #-g object object's set-group-id is set #Example 2: print -n "Enter the pathname of a file: " read pathname if [[ -r $pathname ]] # -r checks for read permission then print "You can read it. " else print "You can not read it. " fi #The following tests compare two objects: #TEST RETURNS TRUE IF... #object1 -nt object2 object1 is newer than object2 #object1 -ot object2 object1 is older than object2 #object1 -ef object2 object1 is another name for object2; -ef #stands for equivalent file #Example 3: if [[ a.out -ot program.c ]] then #a.out is older than program.c, or a.out does not exist cc program.c else #a.out is not older than program.c print "No need to compile program.c; a.out is up-to-date." fi SCRIPT 15 #!/bin/ksh USAGE="Usage=$0" #In this script, we compare the three looping statements of the KornShell. The #following examples produce the same identical output; however, each script uses #a different looping statement to accomplish the task. #While Loop integer n=1 #declare n as an integer; initialize to a 1 #Loop while condition true while ((n <= 4)) do print "$n" ((n = n + 1)) #increment the loop counter done #Above Executes four times #Example until: integer n=1 #like above #Loop until condition becomes true until ((n > 4)) do print "$n" ((n = n + 1)) done #Executes four times also #Example for loop: #The for loop specifies a list of values for a variable #Loop as long as there is another element in the list. By the way # the word "in" is a keyword that separates the name of the #variable from the list of elements. for n in 1 2 3 4 do print "$n" done #Executes four times SCRIPT 16 #!/bin/ksh USAGE="Usage:$0" #This example shows using the colon to mean "always true" integer grand_total=0 #declare and initialize integer grand_total #Start of always loop while : #Potentially infinite loop do print "\nThe sum of all input values is $grand_total " print -n "Enter and integer, or -99 to quit: " read the_number if ((the_number == -99)) #loop termination condition then break #break out of shell else ((grand_total = grand_total + the_number)) #Addition op fi done SCRIPT 17 #!/bin/ksh USAGE="usage:$0" #Example to create a list of all the objects in the current directory. #Use wildcards. The * wildcard is so powerful that it will match every #object, including subdirectories and sockets. Use object tests to #restrict the kinds of objects you wish to match. print "Here is a list of every object in the current directory: " for object in * do print "\t$object" done print "\nHere is a list of every regular file " print "in the current directory: " for object in * do if [[ -f $object ]] # only will print regular files then print "\t$object" fi done print "\nHere is a list of every modifiable C and assembly language " print "source file in the current directory: " for object in *.c *.a do if [[ ( -f $object ) && ( -w $object ) ]] then print "\t$object" fi done #Searching for Objects in Subdirectories print "These objects are two levels underneath $PWD: " #Present Working Dir for object in */* do print "\t$object" done print "$PWD and the directories two levels below it contain" print "the following objects: " for object in * */* */*/* do print "\t$object" done print "$PWD and the next two levels below it contain" print "the following directories: " for object in * */* */*/* do if [[ -d $object ]] then print "\t$object" fi done SCRIPT 18 #!/bin/ksh USAGE="usage: $0" #Demonstrates nested loops #the outer loop generates integers for 11 to 99 integer n=11 while ((n <= 99)) #start of outer loop do answer=prime #start of inner loop. Tests each number to determine #whether n is a prime or not. The values of d (2 ,3,5,7) are #the prime factors for d in 2 3 5 7 # start innner loop do ((remainder = n % d)) if ((remainder == 0)) then answer=composite break #breaks out of inner loop fi done # end of inner loop print "$n is $answer" ((n = n + 1)) done #end outer loop SCRIPT 19 #!/bin/ksh USAGE="Usage:$0" #The select and for statements share the same syntax. Both statements #create loops; however, the primary purpose of the select statement is to #create a looping menu. The elements of list form the menu's entries. So #if list consists of six elements, the menu will contain each of those six #entries. #The select statement places a number in front of each of the menu entries. #After displaying the menu entries, the KornShell automatically prompts the #user to enter a menu entry. You control the text of the promp by assigning a #string to variable PS3. The user then selects a menu entry by typing a number. #Initialize to strings Main_menu_prompt="Main Menu: enter 1, 2 or 3 (or return ) : " submenu_prompt="Submenu: please enter 1 or 2: " #Set PS3 Prompt, necessary for Select Menu PS3="$Main_menu_prompt" #Start Main menu select cmd in "list files" "delete a file" "quit menu" do case $cmd in "list files") PS3="$submenu_prompt" #Now assigning sub prompt select option in "quick list" "detailed list" do case $option in "quick list") ls break;; #leave submenu "detailed list") ls -al break;; #leave submenu *) print "You must enter 1 or 2." break;; esac done PS3="$Main_menu_prompt" #restore prompt "delete a file") print "Which file do you want to delete? " read doomed_file rm "$doomed_file";; #UNIX command to delete files "quit menu") #enter 4 to leave script exit;; *) #if user enters a bad number, print error msg print "You did not enter a valid choice." esac done #end of outer select SCRIPT 20 #!/bin/ksh USAGE="Usage: $0 arg1 arg2 arg3" #NOTICE CHANGE IN USAGE!!! #There are three classes of information that a user can specify on the Kornshell #command line: # 1. Simple arguments ( like numbers, strings, pathnames, etc.) # 2. Single-letter options precede by a minus ( - ) or plus ( + ) sign # 3. I/O redirection operators (like > or < ) #Another name for command line arguments is postional characters. #KornShell provides the following features to help you analyze command #line arguments: # 1. A positional parameter shorthand # 2. A mechanism for setting the values of undefined positional parameters # 3. The shift statement, which does the command line equivalent of a "left #shift" operation # 4. The getopts statement to parse single-character command line options # 5. The set statement, which can set positional parameters independently of #command line args #Positional Parameter Meaning #$0 #if command line argument, name of script # if function call, name of function # if set statement, pathname of the KornShell itself # $1 name of first argument to script, function, or set # $2 name of second argument, function, or set # $3 name of third argument, function, or set #. #. #. # ${10} name of tenth argument to script, function, or set # ${n} name of nth argument to script, function, or set # $# number of currently set positional parameters # $@ or $* expand every positonal parameter, one at a time #The KornShell imposes no limits on the number of positional parameters, but #hardware and operating system contraints will effectively limit this number. print "The name of the shell script is $0" print "The first argument after the shell script name is $1" print "The second argument after the shell script name is $2" print "The third argument after the shell script name is $3" print "Here are all the arguments: " print $* SCRIPT 21 #!/bin/ksh USAGE="usage: $0 arg1 " #This script expects to receive exactly one argument. #If it doesn't, the script reports proper usage #The symbol $# equals the number of arguments if (( $# > 1 )) then print "You passed too many arguments to $0" print "$USAGE" #Contents of the usage statement string" elif (( $# == 1)) then print "You invoked $0 correctly" #Code usually entered here else print "You forgot to specify an argument to $0" print "$USAGE" fi SCRIPT 22 #!/bin/ksh USAGE="usage: $0 [directory name] " #Square brackets mean optional #The following script is very forgiving. The user is suppose to #pass an argument on the command line. However if forgotten, #the script will prompt for the argument. if (( $# > 0 )) then #user has entered one or more command line arguments dir_name=$1 # all other multiple arguments will be ignored else #user has entered zero command line arguments print -n "Enter the name of one directory: " read dir_name fi print "You specified the following directory: $dir_name" if [[ ! -d $dir_name ]] then print "That directory does not exist" fi SCRIPT 23 #!/bin/ksh USAGE="usage: $0 [directory name] " # The following script is a subtle variant of the preceding one. #If the user doesn't specify a directory, then a default value is used. #In this case, /usr/bin is used dir_name=${1:-/usr/bin} print "Checking the following directory: $dir_name" if [[ ! -d $dir_name ]] then print "$dir_name is not a directory" fi SCRIPT 24 #!/bin/ksh USAGE="usage: $0 " # We use the set statement to set arguments #The set statement performs a variety of feats. For now we will #concentrate on its ability to set, unset, or sort positional arguments. #In other words, you don't have to insist that the user type arguments #on the command line in order to get all the features of positional parameters. set red white blue green black pink purple #sets $1 $2 $3 $4 $5 $6 $7 print "Here is a list of $# colors: " for color in "$@" do print "\t$color" done set -s #sorts all the positional characters in lexical order print "Here is the same list, sorted alphabetically: " for color in "$@" do print "\t$color" done set -- #set -- "unsets" all positional parameters print "Where have all the colors gone? Long time passing: " for color in "$@" do print "\t$color" done SCRIPT 25 #!/bin/ksh USAGE="usage: $0 arg1 arg2 arg3 arg4 arg5 " #the shift statement is used here #The shift statement eliminates the leftmost argument(s). Shift slides #all the positonal parameters to the left by one or more positions. #Caution: shifting too far causes a KornShell error. print "You specified $# arguments. " print "The first argument is $1" print shift 1 print "Following a shift, there are only $# arguments." print "The first argument is now $1 ." print shift 2 print "Following a shift 2, there are only $# arguments." print "The first argument is now $1." SCRIPT 25a #!/bin/ksh USAGE="usage: $0 int1 [int2 …intN]" #using $* #Kornshell expands the expressions $* and $@ to mean the value of all the #positional parameters. The expressions $* and $@ are interchangeable. #However, $* and "$*" do have different meanings and so do $@ and "$@". if (($# == 0)) #if user did not specify any arguments, stop script then print $USAGE exit 1 fi #Each time through the loop, the shell will assign the value of the integer #on the command line to variable number for number in $* do ((running_total = running_total + number)) done # Divide running_total by number of arguments. ((average = running_total / $# )) print "The average is $average." SCRIPT 25b #!/bin/ksh #The difference between "$*" and "$@" is subtle, but helpful difference to understand. Here's a script that #explores this difference. #This script introduces the KornShell reserved variable name IFS. Assume that IFS is a string variable to #whichj you can assign a group of characters USAGE="usage: expand.ksh" #Demonstrates "$*" and "$@" #Assign apple to $1, banana bread to $2, and carambola cookies to $3. set apple "banana bread" "carambola cookies" #Kornshell expands "$@" into three elements: "$1" "$2" "$3". #Therefore, the for loop below consists of 3 elements: print \"\$\@\" "expands to three elements:" for element in "$@" do print '\t$element" done #However the shell expands "$*" into one lone element: "$1 $2 $3" print "\n"\"\$\*\" "expands to one element:" for element in "$*" do print "\t$element" done #The first character of the IFS variable is by default a blank space. In the following example #the difference between "$*" and "$@" will be hard to spot. print "\nFirst character of IFS is a blank space: " print "\t"\"\$\@\"": $@" print "\t"\"\$\*\"": $*" #However if the semicolon is placed as the first character of IFS IFS=';,.' #Now the shell will place a semicolon between each expanded element print "\nFirst character of IFS is a blank space: " print "\t"\"\$\@\"": $@" print "\t"\"\$\*\"": $*" #These distinctions also apply to printing arrays set -A flowers gardenia "bird of paradise" hibiscus rose #The following are equivalent ways of printing all the elements of arrays print ${flowers[*]} print ${flowers[@]} #However if the IFS were set to a semicolon and place ${…} within a pair #of double quotes the output is different. print "${flowers[*]} # yields gardenia bird of paradise hibiscus rose print "${flowers[@]} # yields gardenia; bird of paradise; hibiscus; rose SCRIPT 26 #!/bin/ksh USAGE="usage: $0 [-x] [-y] [+-d] [+-q]" #a simple getopts example #A switch (also called an option) is a command line argument that starts #with a - or a + and is followed by a character(s). Use the getopts statement #to analyze switches. Typically the getopts statement is part of a while loop. #The body of the while loop usually contains a case statement. It is the #combination of all three KornShell statements - getopts, while, and case- #that provides a way to analyze switches. #This while loop executes as long as there are switches to evaluate. #Notice the colon : at the beginning of :xydq. This leading colon tells #getopts to : # Set the value of arguments to ? if the user specifies any option other # than those specified. # Set the value of a KornShell reserved variable named OPTARG to the name # of the undefined switch. While getopts :xydq arguments do case $arguments in x) print "You entered -x as an option.";; y) print "You entered -y as an option.";; z) compile=on;; #don't precede d with a minus sign +d) compile=off;; #plus sign are necessary however q) verbose=on;; +q) verbose=off \?) print "$OPTARG is not a valid switch." print "$USAGE";; esac done SCRIPT 27 #!/bin/ksh USAGE="usage: $0 [-x number] [-y number]" #switch arguments # The following script demonstrates how to analyze switch arguments. # A switch argument is a word or number that follows a switch. The user # can fit the argument so that it fits snugly against the switch or can # use spaces To tell getopts that a switch requires a switch argument, # place a colon (: ) after the argument name. Then when you run the # script, the KornShell will assign the switch argument to a reserved # variable named OPTARG. while getopts :x:y: arguments do case $arguments in x) print "You entered -x as an option." argument_to_x=$OPTARG #getopts sets OPTARG to switch argument print "You entered $argument_to_x as an argument to x.";; y) print "You entered -y as an option." argument_to_y=$OPTARG #getopts sets OPTARG to switch argument print "You entered $argument_to_y as an argument to y.";; :) print "You forgot to enter an argument.";; \?) print "$OPTARG is not a valid switch." print "$USAGE";; esac done SCRIPT 28 #!/bin/ksh USAGE="usage: $0]" #simple function with arguments #In this script, the caller passes an integer value to a function named sqr. #Within the confines of function sqr, the KornShell assigns the integer value #to positional parameter $1. The function sqr takes the value ($1) squares it #and prints it. ############################################################ #This function is "sqr". It takes two arguments and prints the result. function sqr { ((s = $1 * $1)) print "$2: the square of $1 is $s." } ############################################################ #Start of main integer an_integer print -n "Enter an integer: " read an_integer print -n "Enter your name: " read your_name #Call above function sqr $an_integer "$your_name" print "Done." SCRIPT 29 #!/bin/ksh USAGE="usage: $0]" #simple function with arguments #The KornShell provides two independent ways for the called function or script #to send information back to the callee: #1. The callee can send one integer value back to the caller using the return #statement. # The return statement assigns an integer value to the predefined variable #$?. The # caller can then use the return variable or ignore it. #2. The callee can send any quantity of type of data back to the caller, if #the caller uses # uses the command output return parameter $(...). This parameter tells #the callee to # pipe all function or script output into a variable selected by the #caller. #Two other methods are available: #1. The function could write information into a global variable and the #caller could read # read that value. Not preferred. #2. The function could write information into a file and the caller read data #out of the file. ############################################### #This function is "sqr". It takes one argument function sqr_it { ((s = $1 * $1)) return $s } #This function is "sqr". It takes one argument function sqr { ((s = $1 * $1)) print "Input -> $1; Output -> $s" } ############################################### #Start of main integer an_integer print -n "Enter an integer: " read an_integer print -n "Enter your name: " read your_name #Call first function above sqr_it $an_integer #The returned value is stored in $? returned_value=$? #must do at this point or will get lost forever print "The square of $an_integer is $returned_value" #Call second function above returned_value=$( sqr $an_integer) #By surrounding the function call in a pair of parenthesis, you've #effectively "bottled " all the outpu from function sqr and stored it #in the variable "returned_value". print "Function sqr returned this value: $returned_value" SCRIPT 30 #!/bin/ksh USAGE="usage: call_scr.ksh" #This script calls another script #Programmers should write every program, command or script #in such a way that it can be easily called by other programs #commmands or scripts. In this example this script calls #another separate script, it's like calling a functiion. #One differnce though is one script does can not access a variable #defined in another script. ############################################################ #!/bin/ksh USAGE="Usage: call_scr.ksh" #This script calls another script print -n "Enter an integer: " read an_integer #Call the script named "square.ksh" and pass the value of an #integer as an argument to it. Must be in path. square.ksh $an_integer ############################################################## #!/bin/ksh USAGE="square.ksh integer" #Pass one argument to this script when called. ((s = $1 * $1)) print "The square of $1 is $s" SCRIPT 31 #!/bin/ksh USAGE="usage: $0" ##################################################################### #This function finds the lowest number in the array function find_minimum { set -s #sort all positional params in lexical order lowest to highest #Since they are sorted, $1 is now the lowest value print "The minimum is $1" } ##################################################################### #Read input and assign it to an array function gather_input { integer counter=0 #Stores values in array as three digit numbers. Pads with 0 if less typeset -Z3 array while read value #reads numbers out of $datafile and assign to array do array[$counter]=$value ((counter = counter + 1 )) done < $1 #read in file with < $1 find_minimum ${array[*]} #call function and pass array as arguments } ######################################################################### #Main #The script begins execution at the next line. print -n "enter the pathname of the data file: " read datafile gather_input $datafile #function call to above. SCRIPT 32 This concept may be a little hard at first to comprehend. To explain autoloading, let me draw an analogy to programming in a compiled language. Suppose you write a large C program containing many functions. The C compiler has to compile every function in that program whether it ends up being called or not. Consequently, you end up with a program that is larger and slower than it theoretically has to be. One solution available on certain operating systems is dynamic (run time) linking. In this solution, the programmer extracts certain functions from the program and places them into one or more function libraries. When the program makes a call to a function not inside the program, the operating system loader utility searches for the desired function inside the function libraries. Autoloading in the Kornshell is a lot like dynamic linking. With Kornshell autoloading , you create one or more directories containing files of functions. Then, you can write Kornshell scripts that call any of these functions, even though the code for these functions isn't actually inside your Kornshell scripts. There are two important benefits: 1. Your Kornshell scripts will run faster because the Kornshell won't have to interpret uncalled functions. That is, Kornshell ignores autoload functions unless the calling script specifically invokes them. 2. You won't have to "reinvent the wheel". That is, every time you create a new function, you can place it inside one of the autoloading directiories Create a file called sqr like below, create a function directory to put it like $HOME/my_funcs ################################################################################## #!/bin/ksh #This is a function named sqr. This function will be executed only if some other script calls it. It is in a separate #file. function sqr { ((s = $1 * $1 )) print "The square of $1 is $s" } #################################################################################### #!/bin/ksh USAGE="usage: callauto.ksh" #Script example to call an autoloaded function #MAIN #Tell script that sqr is defined outside this file: autoload sqr #Assign a directory to FPATH which is the search function path FPATH=$HOME/my_funcs:$FPATH print -n "Enter an integer: " read an_integer #Call function sqr and pass it the value of an_integer answer=$(sqr $an_integer) #Bottled print "$answer" ###################################################################################### SCRIPT 33 This script demonstrates the humble read statement. This shows a couple of new twists. It also shows how to combine a prompt and read into one statement plus the use of the reserved variable called REPLY. #!/bin/ksh USAGE="usage: $0" #You can prompt with a print statement then gather input with a read statement print -n "Enter a string: " read st print "st = $st" #Or you can prompt and gather input in the same statement read name? "Enter a name: " print "$name" #If you specify read and don't specify a variable with it, them the shell assigns the input #to a special variable named REPLY print -n "Enter a country: " read print "The value of REPLY is $REPLY" place=$REPLY print "The value of place is $place" SCRIPT 34 #!/bin/ksh USAGE="Usage: readloop" integer running_total=0 integer a_number print "Enter integer values, one per line, when finished " print "enter an end-of-file mark (usually d or z)" #This loop will execute until the read statement reaches an end-of-file mark while read a_number #read until end of input do ((running_total = running_total + a_number)) done print "\nTotal = $running_total" SCRIPT 35 Each script provides the following three default streams (which can all be redirected). 1. An input stream named standard input and numbered 0. By default, the standard input stream flows from the keyboard to your script. 2. An output stream named standard output and numbered 1. By default, the standard output stream flows from your script to the monitor. 3. An output stream named standard error and numbered 2. By default, the standard error stream flows from your script to the monitor. By default, the print statement sends data on stream number 1 and the read statement gathers data from stream number 0. However, both print and read take an optional argument named -u that allows you to specify the stream number that you are writing to or reading from. For example, to send data out on the standard error stream (number 2), you specify print -u2. In addition to these three default streams, you can use the exec statement to create other input and output streams. To redirect standard output stream, you use the operator >. To redirect the standard error stream , use the same operator .>, but precede it with the stream number, which is 2 Here's a script for C programmers. It compiles C source code and produces two compilation reports: a brief report written to standard output and a detailed report (complete with error messages) written to standard error. #!/bin/ksh USAGE="usage; std_err.ksh Cfile1 [C_fileN] 2> error_file" #On the command line, redirect standard error, stream 2 #demonstrates standard error stream for file in $* do print -n "$file: " # write name of file to standard output print -n -u2 "$file: " # write name of file to standard error #Compile the source code. The cc command automatically sends its errors to std error cc -c $file #The cc command also returns an error status (do not confuse with an error msg) that symbolizes the # success or failure of the compile. An error status of 0 means that there were no errors during the compile #and a nonzero means there were compiler errors if (($? == 0)) then #Write test to standard out and standard error print "no errors" print -u2 "no errors" else print "errors, see error message file." fi done SCRIPT 36 The following script explicitly uses the exec statement to open an input stream from the file poem to the script. The stream number is 8. If you're used to C programming, then you might find the exec statement analogous to the fopen function in the C library. #!/bin/ksh USAGE="usage: openfile.ksh" #Use exec to open a file for reading. exec 8< poem #We have now opened a named input stream from the file "poem" to the script #Read the first three lines of this file. read -u8 first_line read -u8 second_line read -u8 third_line print "$first_line $second_line $third_line" #Using read without -u8 won't work quite as well read first_line < poem #this reads the first line read second_line < poem #this reads the first line also read third_line < poem #this reads the first line also print "$first_line $second_line $third_line" #To verify SCRIPT 37 If you don't explicitly close a stream, the stream will close it for you when the script ends. It's best practice for streams to be opened for only the time needed. #!/bin/ksh USAGE="usage: closefile.ksh" for object in * #list of objects in current directory do if [[ -f $object ]] then print -n "$object: " exec 3< $object #open file $object for reading read -u3 file_line read -u3 second_line read -u3 third_line print "$second_line" exec 3<&- #close file symbolized by 3 fi done SCRIPT 38 Multiple I/O with exec. This is a script that does a line for line comparison of two ascii files. Homegrown diff command #!/bin/ksh USAGE="usage: compare.ksh file1 file2:" if (($# != 2)) #if user forgets command line args, stop and print usage" then print "$USAGE" exit 1 elif [[ (-f $1) && (-f $2) && (-r $1) && (-r $2) ]] then exec 3< $1 exec 4< $2 exec 5> match #open file "match " for output exec 6> nomatch #open file "nomatch" for output else print "$USAGE" exit 1 fi while read -u3 lineA #read a line out of $1 do read -u4 lineB #read a line out of $2 if [ "$lineA" = "$lineB" ] #compare the two lines then print -u5 "$lineA" else print -u6 "$lineA; $lineB" fi done print "DONE!" #OOPS, I forgot to close the files, it's okay this time because it will autoclose on exit