% Shell Scripts: Good Error and Usage messages: $0, $#, $*, and 1>&2 % Ian! D. Allen -- -- [www.idallen.com] % Winter 2015 - January to Apil 2015 - Updated 2019-04-14 18:15 EDT - [Course Home Page] - [Course Outline] - [All Weeks] - [Plain Text] Good Error Messages and Usage Messages ====================================== System administrators see a lot of errors. That's part of their job. When you write a script or a program, the better you make your error messages, the easier it is on your sysadmin. Good shell script error messages must obey these four rules: Must appear on standard error, not standard output: `1>&2` ---------------------------------------------------------- Error messages must appear on standard error, not standard output. : Error messages must not get redirected into output files; they must appear on your terminal so that you know something went wrong. You can use the shell syntax `1>&2` to send to **stderr** any output normally destined for **stdout**. See the examples below. Shell scripts often need to produce their own error messages. Error messages should always be sent to **standard error** so that they appear on your screen and don't get redirected into pipes and output files. Usually `1>&2` is used on output statements, such as `echo`, to send the output text to standard error instead of standard output: echo 1>&2 "$0: Error: expecting one pathname, found $# ($*)" When error messages appear on standard error, they don't get redirected into output files: $ ./myscript.sh >out ./myscript.sh: Error: expecting one pathname, found 0 () We use the shell redirection syntax `1>&2` to re-route `echo` output normally headed for unit 1 (standard output) to actually appear on unit 2 (standard error). Must contain the name of the program from `$0` ---------------------------------------------- Error messages must contain the name of the program that is issuing the message (from `$0`). : Do not put the name of the script into the script; use $0 instead. Scripts get renamed. Never use the actual name of the script in any of the script command lines. Get the name from the `$0` variable: echo 1>&2 "$0: Error: expecting one pathname, found $# ($*)" (For school assignments, you must use the actual name of the script in your submission comment and in the `KEY` line comment, but never in any text that is output by the script. Only in the school comments!) Must state what kind of input was expected ------------------------------------------ Error messages must state how many and what kind of input was expected. : Do not say only "expecting one argument" or "missing parameter", since that doesn't say what *kind* of argument or parameter is needed. Be explicit about exactly what is expected. Examples: `expecting one file name`, or `expecting two email addressses`. *Avoid using only the vague words "argument" and "parameter" in any error message.* echo 1>&2 "$0: Error: expecting one pathname, found $# ($*)" *Never* say just `missing argument` or `illegal input` or `invalid input` or `too many`. Always specify *exactly* what is needed and *how many* is "too many" or "too few". Make your error messages useful! Must display what input the user actually entered ------------------------------------------------- Error messages must display what input the user actually entered. : Display both the number of arguments `$#` and their values `$*` (e.g. `found 3 (a b c)`. Here are examples of good error messages: echo 1>&2 "$0: Expecting 3 file names; found $# ($*)" echo 1>&2 "$0: Student age '$student_age' is not between $min_age and $max_age" echo 1>&2 "$0: Modify days '$moddays' must be greater than zero" echo 1>&2 "$0: File '$file' does not exist; expecting accounting file" Put quotes around anything entered by a user, otherwise your error messages may be confusing. Compare these example messages without and with quotes around the user input file name: $ ./total.sh still ./total.sh: File still does not exist; expecting accounting file Usage: ./total.sh account_file $ ./total.sh system ./total.sh: File system does not exist; expecting accounting file Usage: ./total.sh account_file $ ./total.sh still ./total.sh: File 'still' does not exist; expecting accounting file Usage: ./total.sh account_file $ ./total.sh system ./total.sh: File 'system' does not exist; expecting accounting file Usage: ./total.sh account_file The quotes make it clearer that `still` and `system` are user file names, not just words that are part of the error message. Example ------- A typicall shell script error message: echo 1>&2 "$0: Expecting one file name; found $# ($*)" There are many good properties of the above error message. It gives the user all the information they may need to see what is wrong: - It redirects the error message text to *stderr*: `1>&2` - `$0` is the name used to invoke the script (remember, files can have more than one name so script names shouldn't be hard-coded into the script) - It tells the user exactly what was expected, and how many. The error message does not use the vague, unhelpful word "argument". - It tells the user exactly what was entered: - `$#` is the number of arguments the user actually gave to the script - `$*` shows the actual arguments given to the script, put in parentheses so the user can see spaces, etc. Summary: Four properties of Good Error Messages ----------------------------------------------- Good Error Messages must: 1. Appear on standard error, not on standard output: `1>&2` 2. Give the name of the program (script) that is issuing the error: `$0` 3. Tell the user exactly what was expected by the program (not "argument"). 4. Display exactly what was supplied by the user (using $# and $*). The Usage Message ================= After detecting an error in script usage, the usual thing to do is print a **Good Error Message** explaining the error, followed by a **Usage** message telling how to use the script, then exit the script with a non-zero return code. Don't keep processing bad data! The **Usage** message gives the syntax for correctly using the script, using `man` page syntax to indicate optional and repeated arguments, e.g.: Usage: ./script.sh [ first_line [ last_line ] ] Usage: ./script.sh filename... The name of the script is output using the `$0` variable. Do not hard-code the name of a script into any command inside the script. Only for Usage Errors --------------------- Do not print a **Usage** message for errors unrelated to script usage. If the user of the script used the script correctly, but the script detects an error that is unrelated to correct usage (e.g. no permission, missing file, etc.), then don't print the **Usage** message (because the user did use the script correctly). Only print a **Usage** message if the script is being used incorrectly. -- | Ian! D. Allen, BA, MMath - idallen@idallen.ca - Ottawa, Ontario, Canada | Home Page: http://idallen.com/ Contact Improv: http://contactimprov.ca/ | College professor (Free/Libre GNU+Linux) at: http://teaching.idallen.com/ | Defend digital freedom: http://eff.org/ and have fun: http://fools.ca/ [Plain Text] - plain text version of this page in [Pandoc Markdown] format [www.idallen.com]: http://www.idallen.com/ [Course Home Page]: .. [Course Outline]: course_outline.pdf [All Weeks]: indexcgi.cgi [Plain Text]: 735_good_error_messages.txt [Pandoc Markdown]: http://johnmacfarlane.net/pandoc/