-------------------------------- DAT2330 - Unix - Prof. Ian Allen -------------------------------- REVISED (February 2002) notes on part of Chapter 11. Here are questions you should be able to answer. You will likely find questions similar to these on tests and exams. Chapter 11 - Shell Programming (not all - excerpts) General Notes: For much of the material, the Korn Shell, Bourne Shell, and Bash Shell behave identically. (The C Shell and TCSH are different.) You should fix all the Linux text example shell scripts to conform to the rules for shell scripts. In particular, you should add the missing interpreter line ("#!/bin/sh -u") and set PATH and umask. Error messages should appear on Standard Error (1>&2), and should include the name of the script ($0). Scripts should be run using "./scriptname"; because, you should never rely on "." being in your search PATH. Also, a script's name might match an existing Unix command name, and the Unix command might execute instead of your script. Skip over most of the "Optional" sections in this chapter. Concentrate on the text sections related to material covered in lectures, weekly notes, and in these questions. We are concentrating on basic IF/ELSE and WHILE/UNTIL this term. That means you can skip over these sections of Chapter 11: - "if...then...elif" "for..in" "for" p.374-381 - from "break and continue" to end of chapter p.386-405 Skip over the above sections. - The text puts the "then" keyword of an "if" statement on a separate line. Many people prefer to put it on the same line as the "if": if test "$word1" = "$word2" ; then echo "$0: '$word1' matches '$word2'" else echo "$0: '$word1' does not match '$word2'" fi The semicolon is necessary to end the "test-command". (See the Linux text, bottom of p.373.) - True or False: The "test" command that often immediately follows the word "if" in an "if" statement is a shell built-in command in bash. - What is the usual output (on STDOUT or your screen) of the "test" command? (Try typing some test commands and see what you get.) - True or False: The "test-command" that immediately followes the word "if" in an "if" statement must *always* be a shell built-in command. - True or False: The "test-command" in an "if" statement cannot be a pipe line or contain any redirection. - Look under "Shell variables" in the index for the explanation of the "$#" and "$?" variables. What do these variables contain? - True or False: The "test" command prints "1" or "0" on STDOUT depending on whether the test condition is FALSE or TRUE. - You should double-quote all shell variables to prevent shell glob characters from expanding. The $$, $#, and $? variables are exceptions - they don't really ever need to be quoted. Why? - Why should a script first check to see if a user has supplied all the required arguments (positional parameters) to the script? - What is the difference in return status (exit status) between the commands: $ test 0 = 00 $ test 0 -eq 00 $ test 1 = 01 $ test 1 -eq 01 What shell variable contains the exit status of the previous command? How can you see the contents of this (or any) shell variable? - What happens if you compare strings using -eq in the "test" command? $ test abc -eq def - What is wrong with the following IF statement when used with bash V2? bash# echo "$BASH_VERSION" 2.04.12(1)-release bash$ x=" 123 " bash$ y=" 456 " bash$ if [ "$x" -lt "$y" ]; then echo TRUE ; fi [: 123 : integer expression expected Why is the test command complaining about the value in "$x"? (Not all versions of the test command are this fussy about blanks.) - What happens if you compare numbers using '=' in the "test" command? $ if test 100 = 0100 ; then echo TRUE ; fi - What happens if you give too many arguments to the "test" command? $ test abc def = ghi $ x=* $ test "$x" = "*" $ test $x = "*" This is why you must remember to double-quote your variables! - Which of these commands tests to see if name $x is a directory? $ test -directory "$x" $ test "$x" -d $ test "$x" = "-d" $ test -d "$x" $ test "$x" = "d" $ test "d" -eq "$x" $ test -dir = "$x" How do you test to see if name $x is a file? How do you test to see if name $x is not empty? Are the quotes around $x necessary? - What is wrong with this statement? (This is a frequent student error.) if -w "$x" ; then echo "$0: '$x' is writable" fi Hint: The IF must always be followed by Unix command line to execute. What command name is being executed in the "if" statement? - True or False: The command "test" and the command "[" have identical functionality. They are different names for the same thing. - Note the syntax difference between these two identical-in-function versions of the "test" command: if test A = B ; then ... fi if [ A = B ]; then ... fi The behaviour of the above commands is identical; the second one just looks a bit neater because of the brackets. (If you use the bracket form, you must have the closing bracket. If you use the command-name "test" form, you must NOT have any brackets.) REMEMBER: The brackets need to be seen as individual, separate arguments. Use blanks around them to make them into separate tokens. "if testA = B" is as wrong as "if [A = B]". <== WRONG WRONG WRONG Neither one works because of the missing blanks. (There are no Unix commands with names "testA" or "[A". A valid Unix command name must always follow the "if" keyword.) (Why don't you need blanks around semicolons and pipes?) - Which one of these command lines are not valid Unix command lines? $ [ a = b ] || echo $? $ [ 2 -lt 9 ] || echo $? $ [ -d . ] || echo $? $ [ -f /etc/passwd ] || echo $? $ [ -s /etc/motd ] || echo $? (Hint: Convert the commands from "square bracket" to Unix "test" command syntax.) - What is the output of these command lines? Why? $ [ -f /etc/passwd ] || echo hello $ test -f /etc/passwd || echo hello $ [ -d .. ] || echo hello $ test -d .. || echo hello $ [ -d /etc/passwd ] || echo hello $ test -d /etc/passwd || echo hello $ [ -f .. ] || echo hello $ test -f .. || echo hello - In many shells, this command doesn't output "hi": $ test 9999999999999999999 -gt 999999999999999999 && echo hi (The first number contains 19 digits; the second 18.) Why might this command fail, even though the first number is truly greater than the second number? - True or False: To keep the scripts short, example scripts in the text leave out code that verifies arguments. You must not forget this verification code in your own scripts. - True or False: Unix commands on ACADAIX also use the --help convention to get help on a command's usage, e.g. "ls --help". - The "shift" command (p.374) is mentioned in Chapter 10. Scripts in this course won't need it; but, you can use it if you find it useful. - Many people put the "do" keyword on the same line as the "while" keyword: n=0 while [ "$n" -lt 10 ]; do echo "$0: Number variable n is '$n'" let n=n+1 done The semicolon is necessary to end the "test-command". As with the IF statement, this command can be any Unix command. The return status is all that matters. - Think of the "while" statement as an "if" statement that loops. - True or False: The "test-command" that immediately followes the word "while" in a "while" statement must *always* be a shell built-in command. - True or False: The following loops have identical function: while who | grep alleni >/dev/null ; do echo "alleni is still logged in" sleep 60 done until ! who | grep alleni >/dev/null ; do echo "alleni is still logged in" sleep 60 done (See the explanation of the "!" operator in the weekly notes.) ---------------- The FIND command ---------------- The "find" utility is partially explained on p.378. The examples under "find" in the back of the book are also helpful. Note this (from the man page, and from the first sentence under "Critera" in the back of the Linux text): Numeric arguments can be specified as +n for greater than n, -n for less than n, n for exactly n. Thus: "-size +5" means "size greater than 5 blocks". Thus: "-mtime 5" means "last modified time of exactly 5 days ago". Thus: "-group -5" means "group number less than 5". Concentrate on knowing how to build simple "find" commands (no parentheses or "-or" options), e.g. $ find /bin -mtime -300 -print $ find /bin -mtime 300 -print $ find /bin -mtime +300 -print $ find "$HOME" -size -2 -print $ find "$HOME" -size 2 -print $ find "$HOME" -size +2 -print $ find /tmp /usr/tmp "$HOME/tmp" -name '.bash*' -print $ find /tmp /usr/tmp "$HOME/tmp" -name '*dada*' -print $ find /tmp /usr/tmp "$HOME/tmp" -name '*.c' -print Warning: The AIX version of "find" has a more restricted "-size" option syntax and far fewer options than GNU find. The above examples will work on both systems (and on most other Unix systems since about 1980). - Chapter 11 Chapter Review questions: 1,2,6,9a,9b