Skip to content

Instantly share code, notes, and snippets.

@andrea-calligaris
Last active January 26, 2023 04:11
Show Gist options
  • Save andrea-calligaris/04cfe28ca1068cfa5c82cdf4f3f68976 to your computer and use it in GitHub Desktop.
Save andrea-calligaris/04cfe28ca1068cfa5c82cdf4f3f68976 to your computer and use it in GitHub Desktop.
POSIX portable shell script with arguments [howto: shell script template]
#!/bin/dash
#
# Template for shell scripts, with "help", both short and long options,
# and arguments.
# POSIX compliant, and following best practices.
# Distributed under WTFPL.
this_program_filename=
# Debug mode, true or false.
debug=true
# Options.
recursive=false
force=false
config_file=
verbose_level=0
quit_with_usage_help()
{
cat <<EOF
Usage: $this_program_filename [options] directory
Do something with DIRECTORY.
-r, --recursive recursively do stuff in all directories because yup;
also note the line break formatting and that
there's no period at the end of this sentence
-f, --force force execution even if the computer doesn't wanna
-c, --config FILE specify a configuration file to load
--verbose=LEVEL verbose level 0 to 2 (default 0, don't print anything);
also note: this option doesn't have the short form
It's a good idea to write here some more information about the script. Blah
blah blah; use the option --force (-f) only when you are angry.
The default configuration file is in /etc/path/...
To do X, you can use one of these commands:
$this_program_filename -r .
$this_program_filename ~
However, if the program is very complex, make a man page for it.
Adapted from: <http://mywiki.wooledge.org/BashFAQ/035>
Interesting links:
<https://github.com/koalaman/shellcheck>
<https://stackoverflow.com/a/7948533>
<https://developer.ibm.com/tutorials/l-bash-parameters/>
EOF
# Good width limit is between 70 and 80 columns: --------------------|---------|
exit 0
}
quit_with_error_msg()
{
printf '%s: error: %s\n' "$this_program_filename" "$1" >&2
printf 'Try "%s --help" for more information.\n' \
"$this_program_filename" >&2
exit 1
}
# Get the program name preventing echoing of "./" when running directly.
this_program_filename=$( basename "$0" )
# Parse options.
while :; do
case $1 in
# Good practice to have both '-h' and '--help'.
-h|--help)
quit_with_usage_help
;;
-r|--recursive)
recursive=true
;;
-f|--force)
force=true
;;
-c|--config)
if [ "$2" ]; then
config_file=$2
shift
else
quit_with_error_msg \
'--config option: missing argument.\n'
fi
;;
--verbose=?*)
# Delete everything up to "=" and assign the remainder.
verbose_level=${1#*=}
# Validate.
if [ -z "${verbose_level##*[!0-9]*}" ]; then
quit_with_error_msg \
'--verbose option: expected a positive integer.'
fi
if [ "$verbose_level" -lt 0 ] || [ "$verbose_level" -gt 2 ]
then
quit_with_error_msg '--verbose option: out of range.'
fi
;;
# Handle the case of an empty argument ('--verbose=').
--verbose=)
quit_with_error_msg '--verbose option: missing argument.\n'
;;
# Double-dash: end of options.
--)
shift
break
;;
-?*)
err=$( printf 'invalid option "%s"\n' "$1" )
quit_with_error_msg "$err"
;;
# Default case: no (more) options.
*)
break
esac
shift
done
# Print debug information.
if [ $debug = true ]; then
printf '## Debug info ##\n'
printf 'Variables dump (options should change these):\n'
printf ' > recursive: %s\n' "$recursive"
printf ' > force: %s\n' "$force"
printf ' > config_file: %s\n' "$config_file"
printf ' > verbose_level: %d\n' "$verbose_level"
if [ "$#" -eq 0 ]; then
printf 'No other arguments after the options.\n'
else
printf '%d argument(s) after the options:\n' $#
for var in "$@"
do
printf ' %s\n' "$var"
done
fi
printf '\n'
fi
# Check the parameters after the options.
if [ "$#" -eq 0 ]; then
quit_with_error_msg 'missing operand (the directory).'
elif [ "$#" -gt 1 ]; then
quit_with_error_msg 'too many parameters.'
fi
dir_to_process="$*"
if [ ! -d "$dir_to_process" ]; then
quit_with_error_msg 'No such file or directory.'
fi
# The rest of the code goes here...
dir_to_process=$( readlink -f "$dir_to_process" )
ls -l "$dir_to_process"
# Etc.
#!/bin/dash
clear
printf "\t\t ## Tests for the script ##"
executable="./shell_script_template"
separator="-------------------------------"
prompt=" > "
command="$executable"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable --help"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable /home"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f -r"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f /home -r"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f /home"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f --config my_configuration.cfg /home"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f --config /home"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f -c my_configuration.cfg -r /home"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f -r --verbose=2 /home"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f -r /home ."
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
command="$executable -f -r ba be bi bo bu"
printf "\n\n\n%s\n\n%s%s\n\n" "$separator" "$prompt" "$command"
$command
# Etc.
printf "\n\n\n\n"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment