-------------------------------- DAT2330 - Unix - Prof. Ian Allen -------------------------------- 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 (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. Also skip over these sections: - "if...then...elif" p.374 - "break and continue" p.387 - "BUILTINS" p.397-403 - "FUNCTIONS" p.403-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 in this script? On some systems (Linux) you see output, and on some systems (ACADAIX) you do not. Why? #!/bin/sh -u # Test numeric arguments containing blanks. # x=" 123 " y=" 456 " if [ "$x" -lt "$y" ]; then echo "You don't see this line on ACADAIX. Why?" fi if [ $x -lt $y ]; then echo "You always see this line. Why?" fi - What happens if you compare numbers using '=' in the "test" command? $ test 100 = 0100 - 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: 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 "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]". 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 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 "for" keyword: for x in a b c d ; do echo "$x" done for x do echo "$x" done The semicolon is only needed to end the "list" that follows "in". It isn't needed (or allowed) if there is no "in" list. - True or False: These "for" loops are identical in function: for x do echo "$x" done for x in $* ; do echo "$x" done Hint: What if the first argument to a script containing those loops contained a blank or a shell glob character? - Be very clear on how you want the shell to see the arguments to the "for" loop if the arguments come from inside a shell variable or from a command substitution. Usually, you want the arguments to "for" as separate words, in which case you can't quote the variable containing the arguments, and thus embedded blanks become important and wildcards might expand. This means you must not have variables in "for" loops that could possibly contain unexpected blanks or wildcards! # Userids aren't likely to contain blanks or wildcards. # This unquoted use of $list (userids) is fairly safe: # list=$(who | cut -d ' ' -f 1) # a list of userids for userid in $list ; do echo "one userid is '$userid'" done # File and directory names may easily contain blanks or wildcards. # This unquoted use of $list is sure to fail some day! # Don't do this: # list=$(ls) # a list of non-hidden names (may contain blanks!) for name in $list ; do echo "This is going to fail some day '$name'" done - True or False: If a file or directory name in the current directory contains a blank, the script on the top of p.380 will fail. - 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 shell "case" statement is similar in function to the C Language "switch" statement. It matches a string against a series of shell patterns; the first match is executed and the others are ignored. - Many people write "case" statemens in compact form if the cases are simple and fit on one line: echo 1>&2 "Enter a letter" read letter case "$letter" in [aA]) echo "you entered A" ;; [bB]) echo "you entered B" ;; [cC]) echo "you entered C" ;; *) echo "you did not enter A, B, or C" ;; esac - True or False: These "case" statements are identical: case "$letter" in a|b|c|d|A|B|C|D) echo "hello" ;; esac case "$letter" in [a-dA-D]) echo "hello" ;; esac - True or False: The "test-string" that follows the "case" keyword is identical in function to the "test-command" that follows "if", "while", and "until". It can be any Unix command line. - Type in and touch up the Optional script on p.390. Replace the interpreter with a more standard one and bring the rest of the script up to proper standards. (If you run on ACADAIX, delete the "-e" from the echo commands. That option only works under Linux.) Don't worry about the various escape characters used in the echo command (p.391). Ignore them. Modify the script to accept upper- or lower-case input characters. Modify the script to use any prefix of the command as a case selection pattern, e.g. you could type in "d" or "da" or "dat" or "date" to select the "date" case. Make the script accept any prefix, either upper- or lower-case. (e.g. "P" and "PW" would select the "pwd" case too.) Insert the line "while true; do" before the first "echo" statement and the line "done" after the last line of this script. Indent all the lines in between one tab stop. (VI can do this easily.) Test the script. It should keep looping and prompting you for more input. Add a case "exit" that exits the script when any prefix of "exit" is entered. - Linux text error bottom p.394: Change the last paragraph in the Optional section to read: The shell normally expands special characters that it reads from standard input (e.g. *, $, |). (The "read" builtin does not do this.) If the word that delimits a HERE document is unquoted, e.g. cat <