Shell Script Skeleton is used as a base for writing various utility scripts.
- Simple structure for building shell scripts fast
- Shell Option Parsing
- Supporting both single letters like -p and also long options like --port
- Help Message Functionality
- With command 'help' and options -h or --help
- Version Functionality
- With command 'version' and options -v or --version
- Print functions: ech and err
- Required tools functionality
- Parser functions for replace template variables
{{ VAR }}
Download the shell script from source:
$ git clone git://github.com/z017/shell-script-skeleton.git
Afterwards, run the script:
$ cd shell-script-skeleton
$ ./skeleton
USAGE:
skeleton.sh [options] <command>
OPTIONS:
--help, -h Alias help command
--version, -v Alias version command
--force Don't ask for confirmation
-- Denotes the end of the options. Arguments after this
will be handled as parameters even if they start with
a '-'.
COMMANDS:
help Display detailed help
version Print version information.
If you get an error like this one:
-bash: ./skeleton: Permission denied
Remember to make the script executable:
$ chmod +x skeleton
All scripts must have .sh
extension.
Libraries like _common.sh
must start with _
.
Executables must start with #!/usr/bin/env bash
and no flags. Use set to set shell options so that calling your script as bash script_name.sh
does not break its functionality.
Using bash as the shell scripting language gives us a consistent shell language that's installed on all our machines.
Also, it's recommended to start each file with a top-level comment including a brief overview of its contents.
A copyright notice and author information are optional.
#!/usr/bin/env bash
#
# Generic Shell Script Skeleton.
# Copyright (c) 2015 - Jeremias Longo <jeremiaslongo@gmail.com>
Should be 'readonly', capitalized and with underscores to separate words.
# Constant
readonly PATH_TO_FILES='/some/path'
If we need to export the constant to the environment use 'declare -xr'.
# Set environment constant
declare -xr STAGE="dev"
It's OK to set a constant in getopts or based on a condition, but it should be made readonly immediately afterwards.
VERBOSE='false'
while getopts 'v' flag; do
case "${flag}" in
v) VERBOSE='true' ;;
esac
done
readonly VERBOSE
Should be lower-case and with underscores to separate words.
# Variable
my_var='foo'
Ensure that local variables are only seen inside a function and its children by using local when declaring them. This avoids polluting the global name space and inadvertently setting variables that may have significance outside the function.
Declaration and assignment must be separate statements when the assignment value is provided by a command substitution; as the 'local' builtin does not propagate the exit code from the command substitution.
my_func2() {
local name="$1"
# Separate lines for declaration and assignment:
local my_var
my_var="$(my_func)" || return
# DO NOT do this: $? contains the exit code of 'local', not my_func
local my_var="$(my_func)"
[[ $? -eq 0 ]] || return
}
Should be lower-case and with underscores to separate words.
Braces must be on the same line as the function name and no space between the function name and the parenthesis.
The keyword function is recommended.
function my_func() {
...
}
In order to easily find the start of the program, put the main program in a function called main as the bottom most function. This provides consistency with the rest of the code base as well as allowing you to define more variables as local (which can't be done if the main code is not a function).
The last non-comment line in the file should be a call to main:
main "$@"
Use blank lines between blocks to improve readability. Indent 2 spaces. No tabs.
If you have to write strings that are longer than 80 characters, this should be done with a here document or an embedded newline if possible. Literal strings that have to be longer than 80 chars and can't sensibly be split are ok, but it's strongly preferred to find a way to make it shorter.
# DO use 'here document's
cat <<END;
I am an exceptionally long
string.
END
# Embedded newlines are ok too
long_string="I am an exceptionally
long string."
If a pipeline all fits on one line, it should be on one line.
If not, it can be split at one pipe segment per line with the pipe on the newline and a 2 space indent for the next section of the pipe. This applies to a chain of commands combined using '|' as well as to logical compounds using '||' and '&&'.
# All fits on one line
command1 | command2
# Long commands
command1 \
| command2 \
| command3 \
| command4
Loops in shell are a bit different, but we follow the same principles as with
braces when declaring functions. That is: ; then
and ; do
should be on the same
line as the if/for/while
. else
should be on its own line and closing statements
should be on their own line vertically aligned with the opening statement.
for dir in ${dirs_to_cleanup}; do
if [[ -d "${dir}/${ORACLE_SID}" ]]; then
log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
rm "${dir}/${ORACLE_SID}/"*
if [[ "$?" -ne 0 ]]; then
error_message
fi
else
mkdir -p "${dir}/${ORACLE_SID}"
if [[ "$?" -ne 0 ]]; then
error_message
fi
fi
done
The matching expressions are indented one level from the case
and esac
.
Multiline actions are indented another level with the pattern on a line on its
own, then the actions, then ;;
also on a line of its own.
In general, there is no need to quote match expressions. Pattern expressions
should not be preceded by an open parenthesis. Avoid the ;&
and ;;&
notations.
case "${expression}" in
a)
variable="..."
some_command "${variable}" "${other_expr}" ...
;;
absolute)
actions="relative"
another_command "${actions}" "${other_expr}" ...
;;
*)
err "Unexpected expression '${expression}'"
;;
esac
Simple commands may be put on the same line as the pattern and ;;
as long as the
expression remains readable. Use a space after the close parenthesis of the
pattern and another before the ;;
.
This is often appropriate for single-letter option processing.
verbose='false'
aflag=''
bflag=''
files=''
while getopts 'abf:v' flag; do
case "${flag}" in
a) aflag='true' ;;
b) bflag='true' ;;
f) files="${OPTARG}" ;;
v) verbose='true' ;;
*) error "Unexpected option ${flag}" ;;
esac
done
Shell Script Skeleton is licensed under the MIT License (MIT). Please see License File for more information.