Add gsu_make_tempfile() and fix bug in gui module.
[gsu.git] / README
1 =====
2 <<
3 <img src='gsu.png' alt="gsu logo"><h2>The global subcommand utility</h2>
4 >>
5 =====
6 gsu is a small library of bash functions intended to ease the task of
7 writing and documenting large shell scripts with multiple subcommands,
8 each providing different functionality. gsu is known to work on Linux,
9 FreeBSD, NetBSD and MacOS.
10
11 This document describes how to install and use the gsu library.
12
13 Setting up gsu
14 --------------
15 gsu is very easy to install:
16
17 Requirements
18 ~~~~~~~~~~~~
19 gsu is implemented in bash, and thus gsu depends on bash. Bash version
20 3 is required, version 4 is recommended. Besides bash, gsu depends
21 only on programs which are usually installed on any Unix system (awk,
22 grep, sort, ...). Care has been taken to not rely on GNU specific
23 behavior of these programs, so it should work on non GNU systems
24 (MacOS, *BSD) as well. The gui module depends on the dialog utility.
25
26 Download
27 ~~~~~~~~
28 All gsu modules are contained in a git repository. Get a copy with
29
30 git clone git://git.tuebingen.mpg.de/gsu.git
31
32 There is also a http://git.tuebingen.mpg.de/gsu.git (gitweb) page.
33
34 Installation
35 ~~~~~~~~~~~~
36 gsu consists of several independent modules which are all located
37 at the top level directory of the git repository. gsu requires no
38 installation beyond downloading. In particular it is not necessary
39 to make the downloaded files executable. The library modules can
40 be sourced directly, simply tell your application where to find
41 it. The examples of this document assume that gsu is installed in
42 `/usr/local/lib/gsu' but this is not mandatory.`~/.gsu' is another
43 reasonable choice.
44
45 Conventions
46 -----------
47 Public and private functions and variables
48 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49 Although there is no way in bash to annotate symbols (functions
50 and variables) as private or public, gsu distinguishes between the
51 two. The `gsu_*' name space is reserved for public symbols while all
52 private symbols start with `_gsu'.
53
54 Private symbols are meant for internal use only. Applications should
55 never use them directly because name and semantics might change
56 between gsu versions.
57
58 The public symbols, on the other hand, define the gsu API. This API
59 must not change in incompatible ways that would break existing
60 applications.
61
62 $ret and $result
63 ~~~~~~~~~~~~~~~~
64 All public gsu functions set the $ret variable to an integer value to
65 indicate success or failure. As a convention, $ret < 0 means failure
66 while a non-negative value indicates success.
67
68 The $result variable contains either the result of a function (if any)
69 or further information in the error case. A negative value of $ret is
70 in fact an error code similar to the errno variable used in C programs.
71 It can be turned into a string that describes the error. The public
72 gsu_err_msg() function can be used to pretty-print a suitable error
73 message provided $ret and $result are set appropriately.
74
75 The subcommand module
76 ---------------------
77 This gsu module provides helper functions to ease the repetitious task
78 of writing applications which operate in several related modes, where
79 each mode of operation corresponds to a subcommand of the application.
80
81 With gsu, for each subcommand one must only write a _command handler_
82 which is simply a function that implements the subcommand. All
83 processing is done by the gsu library. Functions starting with the
84 string `com_' are automatically recognized as subcommand handlers.
85
86 The startup part of the script has to source the subcommand file of
87 gsu and must then call
88
89 gsu "$@"
90
91 Minimal example:
92
93 #!/bin/bash
94 com_world()
95 {
96 echo 'hello world'
97 }
98 . /usr/local/lib/gsu/subcommand || exit 1
99 gsu "$@"
100
101 Save this code in a file called `hello' (adjusting the installation
102 directory if necessary), make it executable (`chmod +x hello') and try
103
104 ./hello
105 ./hello world
106 ./hello invalid
107
108 Here, we have created a bash script ("hello") that has a single "mode"
109 of operation, specified by the subcommand "world".
110
111 gsu automatically generates several reserved subcommands, which should
112 not be specified: `help, man, prefs, complete'.
113
114 Command handler structure
115 ~~~~~~~~~~~~~~~~~~~~~~~~~
116 For the automatically generated help and man subcommands to work
117 properly, all subcommand handlers must be documented. In order to be
118 recognized as subcommand help text, comments must be prefixed with
119 two `#' characters and the subcommand documentation must be located
120 between the function "declaration", com_world() in the example above,
121 and the opening brace that starts the function body.
122
123 Example:
124
125 com_world()
126 ##
127 ##
128 ##
129 {
130 echo 'hello world'
131 }
132
133 The subcommand documentation consists of three parts:
134
135 - The summary. One line of text,
136 - the usage/synopsis string,
137 - free text section.
138
139 The three parts should be separated by lines consisting of two # characters
140 only. Example:
141
142 com_world()
143 ##
144 ## Print the string "hello world" to stdout.
145 ##
146 ## Usage: world
147 ##
148 ## Any arguments to this function are ignored.
149 ##
150 ## Warning: This subcommand may cause the top most line of your terminal to
151 ## disappear and may cause DATA LOSS in your scrollback buffer. Use with
152 ## caution.
153 {
154 echo 'hello world'
155 }
156
157 Replace 'hello' with the above and try:
158
159 ./hello help
160 ./hello help world
161 ./hello help invalid
162 ./hello man
163
164 to check the automatically generated help and man subcommands.
165
166 Error codes
167 ~~~~~~~~~~~
168 As mentioned above, all public functions of gsu return an error code
169 in the $ret variable. A negative value indicates failure, and in this
170 case $result contains more information about the error. The same
171 convention applies for subcommand handlers: gsu will automatically
172 print an error message to stderr if a subcommand handler returns with
173 $ret set to a negative value.
174
175 To allow for error codes defined by the application, the $gsu_errors
176 variable must be set before calling gsu(). Each non-empty line in this
177 variable should contain an identifier and error string. Identifiers
178 are written in upper case and start with `E_'. For convenience the
179 $GSU_SUCCESS variable is defined to non-negative value. Subcommand
180 handlers should set $ret to $GSU_SUCCESS on successful return.
181
182 To illustrate the $gsu_errors variable, assume the task is to
183 print all mount points which correspond to an ext3 file system in
184 `/etc/fstab'. We'd like to catch two possible errors: (a) the file
185 does not exist or is not readable, and (b) it contains no ext3 entry.
186 A possible implementation of the ext3 subcommand could look like this
187 (documentation omitted):
188
189 #!/bin/bash
190
191 gsu_errors='
192 E_NOENT No such file or directory
193 E_NOEXT3 No ext3 file system detected
194 '
195
196 com_ext3()
197 {
198 local f='/etc/fstab'
199 local ext3_lines
200
201 if [[ ! -r "$f" ]]; then
202 ret=-$E_NOENT
203 result="$f"
204 return
205 fi
206 ext3_lines=$(awk '{if ($3 == "ext3") print $2}' "$f")
207 if [[ -z "$ext3_lines" ]]; then
208 ret=-$E_NOEXT3
209 result="$f"
210 return
211 fi
212 printf 'ext3 mount points:\n%s\n' "$ext3_lines"
213 ret=$GSU_SUCCESS
214 }
215
216 Printing diagnostic output
217 ~~~~~~~~~~~~~~~~~~~~~~~~~~
218 gsu provides a couple of convenience functions for output. All
219 functions write to stderr.
220
221 - *gsu_msg()*. Writes the name of the application and the given text.
222
223 - *gsu_short_msg()*. Like gsu_msg(), but does not print the application name.
224
225 - *gsu_date_msg()*. Writes application name, date, and the given text.
226
227 - *gsu_err_msg()*. Prints an error message according to $ret and $result.
228
229 Subcommands with options
230 ~~~~~~~~~~~~~~~~~~~~~~~~
231 Bash's getopts builtin provides a way to define and parse command line
232 options, but it is cumbersome to use because one must loop over all
233 given arguments and check the OPTIND and OPTARG variables during each
234 iteration. The gsu_getopts() function makes this repetitive task easier.
235
236 gsu_getopts() takes a single argument: the optstring which contains
237 the option characters to be recognized. As usual, if a character is
238 followed by a colon, the option is expected to have an argument. On
239 return $result contains bash code that should be eval'ed to parse the
240 position parameters $1, $2, ... of the subcommand according to the
241 optstring.
242
243 The shell code returned by gsu_getopts() creates a local variable $o_x
244 for each defined option `x'. It contains `true/false' for options
245 without argument and either the empty string or the given argument for
246 options that take an argument.
247
248 To illustrate gsu_getopts(), assume the above com_ext3() subcommand
249 handler is to be extended to allow for arbitrary file systems, and
250 that it should print either only the mount point as before or the
251 full line of `/etc/fstab', depending on whether the verbose switch
252 `-v' was given at the command line.
253
254 Hence our new subcommand handler must recognize two options: `-t' for
255 the file system type and `-v'. Note that `-t' takes an argument but `-v'
256 does not. Hence we shall use the optstring `t:v' as the argument for
257 gsu_getopts() as follows:
258
259 com_fs()
260 {
261 local f='/etc/fstab'
262 local fstype fstab_lines
263 local -i awk_field=2
264
265 gsu_getopts 't:v'
266 eval "$result"
267 (($ret < 0)) && return
268
269 [[ -z "$o_t" ]] && o_t='ext3' # default to ext3 if -t is not given
270 [[ "$o_v" == 'true' ]] && awk_field=0 # $0 is the whole line
271 fstab_lines=$(awk -v fstype="$o_t" -v n="$awk_field" \
272 '{if ($3 == fstype) print $n}' "$f")
273 printf '%s entries:\n%s\n' "$o_t" "$fstab_lines"
274 ret=$GSU_SUCCESS
275 }
276
277 Another repetitive task is to check the number of non-option arguments
278 and to report an error if this number turns out to be invalid for
279 the subcommand in question. The gsu_check_arg_count() function performs
280 this check and sets $ret and $result as appropriate. This function
281 takes three arguments: the actual argument count and the minimal and
282 maximal number of non-option arguments allowed. The last argument may
283 be omitted in which case any number of arguments is considered valid.
284
285 Our com_world() subcommand handler above ignored any given
286 arguments. Let's assume we'd like to handle this case and
287 print an error message if one or more arguments are given. With
288 gsu_check_arg_count() this can be achieved as follows:
289
290 com_world()
291 {
292 gsu_check_arg_count $# 0 0 # no arguments allowed
293 (($ret < 0)) && return
294 echo 'hello world'
295 }
296
297 Global documentation
298 ~~~~~~~~~~~~~~~~~~~~
299 Besides the documentation for subcommands, one might also want to
300 include an overall description of the application which provides
301 general information that is not related to any particular subcommand.
302
303 If such a description is included at the top of the script, the
304 automatically generated man subcommand will print it. gsu recognizes
305 the description only if it is enclosed by two lines consisting of at
306 least 70 # characters.
307
308 Example:
309
310 #/bin/bash
311
312 #######################################################################
313 # gsu-based hello - a cumbersome way to write a hello world program
314 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
315 # It not only requires one to download and install some totally weird
316 # git repo, it also takes about 50 lines of specially written code
317 # to perform what a simple echo 'hello world' would do equally well.
318 #######################################################################
319
320 HTML output
321 ~~~~~~~~~~~
322 The output of the auto-generated man subcommand is a suitable input for the
323 grutatxt plain text to html converter. Hence
324
325 ./hello man | grutatxt > index.html
326
327 is all it takes to produce an html page for your application.
328
329 Interactive completion
330 ~~~~~~~~~~~~~~~~~~~~~~
331 The auto-generated `complete' subcommand provides interactive bash
332 completion. To activate completion for the hello program, it is
333 enough to put the following into your `~/.bashrc':
334
335 _hello()
336 {
337 eval $(hello complete 2>/dev/null)
338 }
339 complete -F _hello hello
340
341 This will give you completion for the first argument of the hello
342 program: the subcommand.
343
344 In order to get subcommand-sensitive completion you must provide a
345 _completer_ in your application for each subcommand that is to support
346 completion. Like subcommand handlers, completers are recognized by name:
347 If a function xxx_complete() is defined, gsu will call it on the
348 attempt to complete the `xxx' subcommand at the subcommand line. gsu
349 has a few functions to aid you in writing a completer.
350
351 Let's have a look at the completer for the above `fs' subcommand.
352
353 complete_fs()
354 {
355 local f='/etc/fstab'
356 local optstring='t:v'
357
358 gsu_complete_options $optstring "$@"
359 (($ret > 0)) && return
360
361 gsu_cword_is_option_parameter $optstring "$@"
362 [[ "$result" == 't' ]] && awk '{print $3}' "$f"
363 }
364
365 Completers are always called with $1 set to the index into the array
366 of words in the current command line when tab completion was attempted
367 (see `COMP_CWORD' in the bash manual). These words are passed to the
368 completer as $2, $3,...
369
370 gsu_complete_options() receives the option string as $1, the word
371 index as $2 and the individual words as $3, $4,... Hence we may simply
372 pass the $optstring and `"$@"'. gsu_complete_options() checks if the
373 current word begins with `-', i.e., whether an attempt to complete
374 an option was performed. If yes gsu_complete_options() prints all
375 possible command line options and sets $ret to a positive value.
376
377 The last two lines of complete_fs() check whether the word preceding
378 the current word is an option that takes an argument. If it is,
379 that option is returned in $result, otherwise $result is the empty
380 string. Hence, if we are completing the argument to `-t', the awk
381 command is executed to print all file system types of /etc/fstab as
382 the possible completions.
383
384 See the comments to gsu_complete_options(),
385 gsu_cword_is_option_parameter() and gsu_get_unnamed_arg_num()
386 (which was not covered here) in the `subcommand' file for a more
387 detailed description.
388
389 The gui module
390 --------------
391 This module can be employed to create interactive dialog boxes from a
392 bash script. It depends on the dialog(1) utility which is available on
393 all Unix systems. On Debian and Ubuntu Linux it can be installed with
394
395 apt-get install dialog
396
397 The core of the gui module is the gsu_gui() function which receives
398 a _menu tree_ as its single argument. The menu tree defines a tree
399 of menus for the user to navigate with the cursor keys. As for a
400 file system tree, internal tree nodes represent folders. Leaf nodes,
401 on the other hand, correspond to _actions_. Pressing enter activates a
402 node. On activation, for internal nodes a new menu with the contents of
403 the subfolder is shown. For leaf nodes the associated _action handler_
404 is executed.
405
406 Hence the application has to provide a menu tree and an action handler
407 for each leaf node defined in the tree. The action handler is simply a
408 function which is named according to the node. In most cases the action
409 handler will run dialog(1) to show some dialog box on its own. Wrappers
410 for some widgets of dialog are provided by the gui module, see below.
411
412 Menu trees
413 ~~~~~~~~~~
414 The concept of a menu tree is best illustrated by an example. Assume
415 we'd like to write a system utility for the not-so-commandline-affine
416 Linux sysadmin next door. For the implementation we confine ourselves
417 with giving some insight in the system by running lean system commands
418 like `df' to show the list of file system, or `dmesg' to print the
419 contents of the kernel log buffer. Bash code which defines the menu
420 tree could look like this:
421
422 menu_tree='
423 load_average
424 processes
425 hardware/
426 cpu
427 scsi
428 storage/
429 df
430 mdstat
431 log/
432 syslog
433 dmesg
434 '
435
436 In this tree, `hardware/', `block_devices/' and `log/' are the only
437 internal nodes. Note that these are written with a trailing slash
438 character while the leaf nodes have no slash at the end. All entries
439 of the menu tree must be indented by tab characters.
440
441 Action handlers
442 ~~~~~~~~~~~~~~~
443 Action handlers are best explained via example:
444
445 Our application, let's call it `lsi' for _lean system information_,
446 must provide action handlers for all leaf nodes. Here is the action
447 handler for the `df' node:
448
449 lsi_df()
450 {
451 gsu_msgbox "$(df -h)"
452 }
453
454 The function name `lsi_df' is derived from the name of the script
455 (`lsi') and the name of the leaf node (`df'). The function simply
456 passes the output of the `df(1)' command as the first argument to
457 the public gsu function gsu_msgbox() which runs dialog(1) to display
458 a message box that shows the given text.
459
460 gsu_msgbox() is suitable for small amounts of output. For essentially
461 unbounded output like log files that can be arbitrary large, it is
462 better to use gsu_textbox() instead which takes a path to the file
463 that contains the text to show.
464
465 To illustrate gsu_input_box() function, assume the action handler
466 for the `processes' leaf node should ask for a username, and display
467 all processes owned by the given user. This could be implemented
468 as follows.
469
470 lsi_processes()
471 {
472 local username
473
474 gsu_inputbox 'Enter username' "$LOGNAME"
475 (($ret != 0)) && return
476 username="$result"
477 gsu_msgbox "$(pgrep -lu "$username")"
478 }
479
480 Once all other action handlers have been defined, the only thing left
481 to do is to source the gsu gui module and to call gsu_gui():
482
483 . /usr/local/lib/gsu/gui || exit 1
484 gsu_gui "$menu_tree"
485
486 Example
487 ~~~~~~~
488 The complete lsi script below can be used as a starting point
489 for your own gsu gui application. If you cut and paste it, be
490 sure to not turn tab characters into space characters.
491
492 #!/bin/bash
493
494 menu_tree='
495 load_average
496 processes
497 hardware/
498 cpu
499 scsi
500 storage/
501 df
502 mdstat
503 log/
504 syslog
505 dmesg
506 '
507
508 lsi_load_average()
509 {
510 gsu_msgbox "$(cat /proc/loadavg)"
511 }
512
513 lsi_processes()
514 {
515 local username
516
517 gsu_inputbox 'Enter username' "$LOGNAME"
518 (($ret < 0)) && return
519 username="$result"
520 gsu_msgbox "$(pgrep -lu "$username")"
521 }
522
523 lsi_cpu()
524 {
525 gsu_msgbox "$(lscpu)"
526 }
527
528 lsi_scsi()
529 {
530 gsu_msgbox "$(lsscsi)"
531 }
532
533 lsi_df()
534 {
535 gsu_msgbox "$(df -h)"
536 }
537
538 lsi_mdstat()
539 {
540 gsu_msgbox "$(cat /proc/mdstat)"
541 }
542
543 lsi_dmesg()
544 {
545 local tmp="$(mktemp)" || exit 1
546
547 trap "rm -f $tmp" EXIT
548 dmesg > $tmp
549 gsu_textbox "$tmp"
550 }
551
552 lsi_syslog()
553 {
554 gsu_textbox '/var/log/syslog'
555 }
556
557 . /usr/local/lib/gsu/gui || exit 1
558 gsu_gui "$menu_tree"
559
560 The config module
561 -----------------
562 Some applications need config options which are not related to
563 any particular subcommand, like the URL of a web service, the path
564 to some data directory, or a default value which is to be used by
565 several subcommands. Such options do not change frequently and are
566 hence better stored in a configuration file rather than passed to
567 every subcommand that needs the information.
568
569 The config module of gsu makes it easy to maintain such options and
570 performs routine tasks like reading and checking the values given in
571 the config file, or printing out the current configuration. It can
572 be used stand-alone, or in combination with either the subcommand or
573 the gui module.
574
575 Defining config options
576 ~~~~~~~~~~~~~~~~~~~~~~~
577 To use the config module, you must define the $gsu_options bash array.
578 Each config option is represented by one slot in this array. Here is
579 an example which defines two options:
580
581 gsu_options=(
582 "
583 name=fs_type
584 option_type=string
585 default_value=ext3
586 required=false
587 description='file system type to consider'
588 help_text='
589 This option is used in various contexts. All
590 subcommands which need a file system type
591 use the value specified here as the default.
592 '
593 "
594 "
595 name=limit
596 option_type=num
597 default_value=3
598 required=no
599 description='print at most this many lines of output'
600 "
601 )
602
603 Each config option consists of the following fields:
604
605 - *name*. This must be a valid bash variable name. Hence no special
606 characters are allowed.
607
608 - *option_type*. Only `string' and `num' are supported but additional
609 types might be supported in future versions. While string variables
610 may have arbitrary content, only integers are accepted for variables
611 of type `num'.
612
613 - *default_value*. The value to use if the option was not specified.
614
615 - *required*. Whether gsu considers it an error if the option was
616 not specified. It does not make sense to set this to `true' and set
617 *default_value* at the same time.
618
619 - *description*. Short description of the variable. It is printed by
620 the `prefs' subcommand.
621
622 - *help_text*. Optional long description, also printed by `prefs'.
623
624 To enable the config module you must source the config module of gsu
625 after $gsu_options has been defined:
626
627 . /usr/local/lib/gsu/config || exit 1
628
629 Passing config options to the application
630 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
631 There are two ways to pass the value of an option to a gsu application:
632 environment variable and config file. The default config file is
633 ~/.$gsu_name.rc where $gsu_name is the basename of the application,
634 but this can be changed by setting $gsu_config_file. Thus, the
635 following two statements are equivalent
636
637 fs_type=xfs hello fs
638 echo 'fs_type=xfs' > ~/.hello.rc && hello fs
639
640 If an option is set both in the environment and in the config file,
641 the environment takes precedence.
642
643 Checking config options
644 ~~~~~~~~~~~~~~~~~~~~~~~
645 The gsu config module defines two public functions for this purpose:
646 gsu_check_options() and gsu_check_options_or_die(). The latter function
647 exits on errors while the former function only sets $ret and $result
648 as appropriate and lets the application deal with the error. The best
649 place to call one of these functions is after sourcing the config
650 module but before calling gsu() or gsu_gui().
651
652 Using config values
653 ~~~~~~~~~~~~~~~~~~~
654 The name of an option as specified in $gsu_options (`fs_type' in
655 the example above) is what users of your application may specify at
656 the command line or in the config file. This leads to a mistake that
657 is easy to make and difficult to debug: The application might use a
658 variable name which is also a config option.
659
660 To reduce the chance for this to happen, gsu_check_options() creates
661 a different set of variables for the application where each variable
662 is prefixed with ${gsu_name}. For example, if $gsu_options as above
663 is part of the hello script, $hello_fs_type and $hello_limit are
664 defined after gsu_check_options() returned successfully. Only the
665 prefixed variants are guaranteed to contain the proper value, so this
666 variable should be used exclusively in the application. The
667 prefix may be changed by setting $gsu_config_var_prefix before calling
668 gsu_check_options().
669
670 com_prefs()
671 ~~~~~~~~~~~
672 For scripts which source both the subcommand and the config module, the
673 auto-generated 'prefs' subcommand prints out the current configuration
674 and exits. The description and help text of the option as specified
675 in the `description' and `help_text' fields of $gsu_options are shown
676 as comments in the output. Hence this output can be used as a template
677 for the config file.
678
679 List of public variables
680 ------------------------
681 - *$gsu_dir*. Where gsu is installed. If unset, gsu guesses
682 its installation directory by examining the $BASH_SOURCE array.
683
684 - *$gsu_name*. The name of the application. Defaults to $0 with
685 all leading directories removed.
686
687 - *$gsu_banner_txt*. Used by both the subcommand and the gui
688 module. It is printed by the man subcommand, and as the title for
689 dialog windows.
690
691 - *$gsu_errors*. Identifier/text pairs for custom error reporting.
692
693 - *$gsu_config_file*. The name of the config file of the application.
694 Defaults to `~/.${gsu_name}.rc'.
695
696 - *$gsu_options*.
697
698 - *$gsu_config_var_prefix*. Used by the config module to set up
699 the variables defined in $gsu_options.
700
701 License
702 -------
703 gsu is licensed under the GNU LESSER GENERAL PUBLIC LICENSE (LGPL), version 3.
704 See COPYING and COPYING.LESSER.
705
706 Contact
707 -------
708 Send beer, pizza, patches, improvements, bug reports, flames,
709 (in this order), to Andre Noll `<maan@tuebingen.mpg.de>'.
710
711 References
712 ----------
713 - http://www.gnu.org/software/bash/bash.html (bash)
714 - http://www.invisible-island.net/dialog/dialog.html (dialog)
715 - http://triptico.com/software/grutatxt.html (grutatxt)