-------------------------------------------------- Practice Unix/Linux Questions - Chapters 5, 10, 11 -------------------------------------------------- -IAN! idallen@ncf.ca All scripts written must begin with a proper script header, including the correct interpreter, comments, and a proper PATH and umask. All scripts must work on both Linux (Linux Test Machine) and on AIX Unix. (The same script must work in both places without modification, unless otherwise indicated.) All prompts for user input must appear on the terminal, even if standard output is redirected. (Do not send prompts for input into the output file!) Test this! Where possible, commands should open their own files. (Do not use "cat" needlessly - read more on this point in the week4.txt notes.) Never let the shell expand glob patterns (wildcards) unexpectedly. Test this! Don't assume you can write temporary files into the current directory. Test this by changing directories to a directory into which you cannot write and running your script from there. Pay close attention to the names and locations of output files. Real scripts must check the error returns on the commands they use. At minimum, exit the script if an important command fails. Not all of these scripts are equally difficult to write. Find the easy ones first! Problems in this file have been given a complexity rating from 1-5, with 1 being "really simple" and 5 being "complex". TimeSaver: Use filename completion in the shell to complete the names of these scripts when you want to test them or edit them. Don't keep typing the full path names of the scripts! Let your Shell do your typing for you! --) Type in all the scripts in the parts of Chapter 11 that we study. Complexity: 1 Start with "if1" and "chkargs". Add the correct script headers to the scripts. Fix the prompts. Test them to make sure that they work. --) Write this executable script named "10_top_five.sh" Complexity: 1 Verify that the script has exactly one command line argument. Verify that the argument is a file, and that it is readable. (Print an error message and exit the script if these things are not true.) Sort the file and display the first five lines. Bonus: Prompt and read the file name if none is given on the command line. --) Write this executable script named "11_file_lines_counter.sh" Complexity: 1 From the command line arguments, select only arguments that are readable file names. (Print warnings and skip over non-files or unreadable arguments.) Output one line per file in this format: $ ./foo one . two .. nosuch The file 'one' contains 27 lines. Ignored non-file '.'. The file 'two' contains 123 lines. Ignored non-file '..'. Ignored non-file 'nosuch'. --) Write this executable script named "12_string_compare.sh" Complexity: 1 Prompt for and read a string. Compare this string against each one of the command line arguments and print whether or not the string is an exact match: $ ./foo a two "this is three" Input: this is three You entered: this is three String 'this is three' does not match argument 'a'. String 'this is three' does not match argument 'two'. String 'this is three' matches argument 'this is three'. --) Write this executable script named "13_many_file_mailer.sh" Complexity: 2 Verify that all the command line arguments are names for non-empty, readable files. Print an error message and exit if this is not true. Prompt for and read an email userid. Email the content of each of the files (each argument on the command line) to the given userid, one per email message. The script must check to see that the mail command works each time it is used. Bonus: Validate all the command line arguments and print *all* the errors, and then exit, instead of exiting on the first error (and thus not finding other errors in other arguments). --) Write this executable script named "14_optional_arguments_demo.sh" Complexity: 2 This script takes zero to three arguments. Without arguments, the contents of three variables are displayed. Each argument replaces one of the variables: With no arguments, display: PWD PATH HOME With 1 argument, display: arg1 PATH HOME With 2 arguments, display: arg1 arg2 HOME With 3 arguments, display: arg1 arg2 arg3 $ ./foo Output is: /home/idallen/tmp /bin:/usr/bin /home/idallen $ ./foo blue Output is: blue /bin:/usr/bin /home/idallen $ ./foo blue green Output is: blue green /home/idallen $ ./foo blue green red Output is: blue green red $ ./foo blue green red yellow ./foo: Expecting less than 4 arguments; found 4 (blue green red yellow) Restrictions: You may only expand $PWD, $PATH, and $HOME *once* each in the script. You may only use *one* echo command to produce the output and *one* echo command to produce the error message. Hints (encrypted using rot13 on all the letters): Hfr guerr inevnoyrf gb ubyq gur guerr bhgchg inyhrf. Vavgvnyvmr gur inevnoyrf gb gur inyhrf bs CJQ, CNGU, naq UBZR ng gur fgneg bs gur fpevcg. Qrcraqvat ba gur ahzore bs nethzragf, ercynpr bar be zber bs gur inevnoyrf jvgu gur inyhrf sebz gur pbzznaq yvar. Gur pbafgehpg "pnfr $# va" vf hfrshy urer. Ng gur raq bs gur fpevcg, hfr gur inevnoyrf va lbhe "rpub" bhgchg yvar. --) Write this executable script named "15_file_sort_self_compare.sh" Complexity: 2 Verify that the script has exactly one or zero command line file name arguments. If there is no command line argument, prompt for and read a filename. Verify that the argument or input is a file, and that it is readable. Sort the file into the /tmp directory under a temporary name. (Use $$ somewhere in the name to help ensure uniqueness.) Run a "diff" on the original file and the temporary file and count the number of lines of output. The output should look like this: $ ./foo /etc/resolv.conf Processing: /etc/resolv.conf The file differs from its sorted version by 4 lines. $ ./foo Enter a file name: /etc/passwd Processing: /etc/passwd The file differs from its sorted version by 69 lines. Remove the temporary file when you are done with it. The script must check to make sure that the sort worked. Bonus: Handle multiple command line arguments (multiple filenames), not just one. (You don't have to handle zero arguments in this case, since you haven't learned how to alter the argument list in a script.) --) Write this executable script named "16_comment_extractor.sh" Complexity: 2 Verify that the script has exactly one command line argument, that it is a file, and that it is readable. Extract and display all the comment lines (starting with "#") from the argument file. Bonus: Don't display lines that begin "#!". Second Bonus: Handle multiple command line arguments (multiple filenames), not just one. Print the name of the file before its list of comments. Skip over (with a warning) nonexistent or non-file pathnames. $ ./foo a.sh b.sh no.such File a.sh: # $0 (no arguments) # A la la script la la la la etc. la la la. # -IAN! idallen@ncf.ca June 1988 File b.sh: # $0 [pathname] # Some other kind of script la la la ho ho ho. # -IAN! idallen@ncf.ca June 1998 ./foo: 'no.such' is not a readable file. --) Write this executable script named "17_script_interpreter_validator.sh" Complexity: 2 Extract the first line of the script file that is the first command line argument. Validate this first line. To be valid, it must be one of the lines you use in your scripts to date. Print a special message for invalid first lines that are blank. Make sure that each command line argument you process is a readable file - don't try to process directories or unreadable files given as arguments, print a warning and skip over them. $ head -3 script1.sh #!/bin/sh -u # $0 (no arguments) # This script does the following: $ ./foo script1.sh Processing: script1.sh Found this first line: #!/bin/sh -u This is a valid first line of a script. $ head -3 script2.sh #/bin/sh -u # $0 pathname # This script does the following: $ ./foo script2.sh Processing: script2.sh Found this first line: #/bin/sh -u *** This is not a valid first line of a shell script. *** $ touch empty.sh $ ./foo empty.sh Processing: empty.sh Found this first line: *** The first line of this script is missing or empty. *** Suggestion: Use a case statement to compare the line you found with all possible valid first lines; each valid line becomes a separate pattern to match against the line extracted from the script, e.g.: "#!/bin/sh -u") echo good! ;; If none of the valid lines match, the default case will print the error message. Bonus: Handle multiple command line arguments (multiple files), not just one. Validate the first lines of all of the argument files. Skip over (with a warning) non-files or unreadable files. --) Write this executable script named "18_userid_validator.sh" Complexity: 2 Process all the arguments on the command line. For each argument, validate it as being of the correct standard form for an Algonquin student email userid. Algonquin userids must be exactly 8 characters long (with some odd exceptions). Up to four letters of the last name start the userid; the rest of the 8 characters must be digits. Some userids may be shorter than 8 characters because they contain only 4 digits after a short last name. (Our class list contains two such userids.) Print warnings about them if you find one. Sample classifications: abcd0001 - valid abc00001 - valid ab000001 - valid abc0001 - valid, but print warning ab0001 - valid, but print warning a001 - invalid ab001 - invalid abcd001 - invalid (must have at least 4 digits) a0001 - invalid a0000001 - invalid (no single-character last names!) abcde001 - invalid (only max 4 letters allowed) abcd00001 - invalid (only max 8 characters allowed) abc@0001 - invalid abc_0001 - invalid abc.0001 - invalid 0001abcd - invalid a0a0a0a0 - invalid abc00abc - invalid Sample script usage: $ ./foo abcd0001 ab01 "" "*" alleni abc0001 Argument 'abcd0001' is valid *** Argument 'ab01' is invalid *** Argument '' is invalid *** Argument '*' is invalid *** Argument 'alleni' is invalid Argument 'abc0001' is valid (Warning: short format) Hints (encrypted using rot13 on all the letters): Guvf vf rnfl gb qb hfvat n "pnfr" fgngrzrag gb cnggrea zngpu gur srj (5) inyvq hfrevq flagnkrf ntnvafg rnpu nethzrag. Vs abar bs gur inyvq cnggreaf zngpu, gur hfrevq zhfg or vainyvq. Gur "pnfr" cnggreaf ner znqr hc bs fbzr ahzoref bs yrggref sbyybjrq ol fbzr ahzoref bs qvtvgf. Bonus: Handle arguments that are either plain userids with no domain name attached (as above), or are userids with attached domain names of "@algonquincollege.com", e.g. "abcd0001@algonquincollege.com". Any attached domain that is not correct should result in an "invalid" classification, even if the userid is of the correct form. (Hint: duplicate your existing tests, but add the domain to the end.) Second Bonus: Redo the script to output *all* the valid userids with attached Algonquin domain names, whether or not the domain names were supplied with the userids. Send all errors and warnings to stderr. Redirecting output will give you a list of "approved" userids: $ ./foo abcd0001 ab01 "" "*" alleni abc0001 abcd9999@ncf.ca >out *** Argument 'ab01' is invalid *** Argument '' is invalid *** Argument '*' is invalid *** Argument 'alleni' is invalid *** Warning: 'abc0001' is a valid short form *** Argument 'abcd0001@ncf.ca' is invalid $ cat out abcd0001@algonquincollege.com abc0001@algonquincollege.com Amuse yourself by splitting up the passwd file into words and feeding it as command line arguments to your script (don't try this on ACADAIX - the passwd file is big and it will load down the machine - do it on my Linux machine only): $ ./foo $(tr ':' ' ' out ...lots and lots of errors... $ cat out | sort -u ...a nice list of Algonquin userids... --) Write this executable script named "19_email_validator.sh" Complexity: 2 Process all the arguments on the command line. For each argument, validate it as being of the correct standard form for an Internet email address. No blanks are allowed, and only one "@" is permitted. The userid part cannot be empty, and the host part cannot be empty. Sample classifications: a@b - valid a - invalid (missing host) a@ - invalid (missing host) @a - invalid (missing userid) @ - invalid (missing userid and missing host) a@@b - invalid (only one @ allowed) got blanks@foo.com - invalid (blanks not allowed) foo@more blanks.com - invalid (blanks not allowed) Hints (encrypted using rot13 on all the letters): Ntnva, guvf pna or cebtenzzrq hfvat n fvzcyr frg bs "pnfr" cnggreaf gung pna or nccyvrq gb gur nethzrag gb qrpvqr vs vg zrrgf gur pevgrevba va dhrfgvba. Or pnershy nobhg trggvat gur beqre bs gur "pnfr" fgngrzragf naq cnggreaf pbeerpg. (Lbh znl arrq zber guna bar "pnfr" fgngrzrag gb cresbez nyy gur pynffvsvpngvbaf.) --) Write this executable script named "20_bulk_mailer.sh" Complexity: 3 Run "rusers" to the AIX Unix Machine. Extract only the userids. (Get rid of the first word, which is the machine name.) Remove duplicate userids. (See the Hints for help.) For each userid on the machine, pretend to send them an email message. (Do not actually send the email!) The contents of the email message is in the file given as the only command-line argument. Sample: $ echo "Hello there." >msg.txt $ ./1_bulk_mailer.sh msg.txt Your bulk message file contains: Hello there. I would send to these userids on acadaix.algonquincollege.com: mail abcd0001@acadaix.algonquincollege.com