Just a Unix Shell script Template
just.sh version 0.0.7
just.bash version 0.0.9
This is intended to provide a template for writing shell scripts. It gathers some arguably good and bad practices I've acquired over the years to handle command line arguments and inline documentation.
There are two versions of this script:
just.sh - a template that should would with most sh/bash compatible shells
just.bash - a template that uses bash 4.0 and greater features like associative arrays
The goals of this script template are to:
Provide a command line processor that:
- Can handle both short (e.g -h) and long command line arguments (e.g --help)
- Is able to process multiple actions and options so the script doesn't need to be called multiple times
- Multiple actions and options are comma separated
- Uses inline tags to semi-auto document script for the help/usage argument
Provide a base set of functionality that has:
- Help information
- Version information
- Debug and verbose command-line arguments/options to help with code debugging/quality
- Some additional base code checking capability (call out to shellcheck)
- Dry-run mode capability
- The ability to split larger scripts into modules which are loaded at run time
This section details some of the choices taken in the template.
- Use true/false as values rather than 0/1 as I often generate YAML and other config files that use these as values
- Put environment variables and command line parameters into variables so that they are given more interpretable names
- Only run inline information gathering (version, help, etc) when called to reduce start up time
The script has the following workflow
- Get some environment variables
- If given no switches/parameters print help and exit
- Set defaults
- Parse command line switches/parameters
- If options switch has values process them
- Reset/updates defaults base on command line switches/parameters
- Perform action(s)
Modules can be put in a sub directory (or a defined directory) and loaded at run time. This gives the ability to break up a large script into modules for easier management. The modules need to have a .sh extension to be found by the script. This can be changed.
As previously mentioned defaults are specified in the set_defaults function, and updated in the reset_defaults function once the switches/parameters and their values are processed and options are processed. As previously stated, I've used true/false as values rather than 0/1 as I often generate YAML and other config files that use these as values, so it makes coding a bit more straightforward/transparent.
In general output is run through the verbose_message function, which handles formatting and outputs appropriate prefixes, e.g. "Notice", "Warning", etc. This is designed to make sure out is consistently formatted. It also allows output to be done only when the verbose mode is set so the script runs quietly if needed.
There is a do_exit function that doesn't exit when in dry-run mode. This can be handy for debugging your code when you are running in dry-run mode where you are not executing commands in the shell but want to test the flow.
There is a check_values function that can be run with switches that take values. This is a simple check to see if the value, if it starts with a "--" then it assumes that you haven't provided a value and it's processing the next switch and exit. This can be overridden by using the --force switch.
There is an execute_command function that will output the command when run in verbose mode, and not execute the command when run in dry-run mode. This is useful for debugging and testing workflow.
The print_help function outputs information about the standard switches. To reduce the amount of duplication, i.e. having the case statement, then having to duplicate this in a separate help function, and thus have to keep the two in sync, you can use tags in the case statement, which the print_help function will process to print help information.
The format of the embedded help information tags is the case followed by a hash, and a commented description under the case, e.g.
--action*) # switch
# Action to perform
check_value "$1" "$2"
actions="$2"
do_actions="true"
shift 2
;;
When the --help, or --usage switch is used, and the the print_help function called, it parses this to produce:
Usage: just.sh --switch [value]
switches:
--------
--action*)
Action to perform
Like the print_help function, print_options works on tags, e.g.
debug) # option
# Enable debug mode
do_debug="true"
;;
When the --usage switch is used with the options value, this produces:
Usage: just.sh --option(s) [value]
options:
-------
debug)
Enable debug mode
Multiple options can be specified at the one time by separating them with a comma, e.g.
./just.sh --options option1,option2
Like the print_help function, print_actions works on tags, e.g.
help) # action
# Print actions help
print_actions
exit
;;
When the --usage switch is used with the actions value, this produces:
Usage: just.sh --action(s) [value]
actions:
-------
help)
Print actions help
Multiple actions can be specified at the one time by separating them with a comma, e.g.
./just.sh --actions action1,action2
The benefit of being able to run multiple actions means you don't need to run the script multiple times. Obviously some caution needs to be taken in ordering the workflow in sequential operations so they proceed in the correct order.