#+SETUPFILE: ../../../template/level-2.org
#+TITLE: UNIX-like Shell Scripting Best Practices
#+DATE: <2019-12-27 Fri 22:55>
#+AUTHOR: vaeringjar
#+EMAIL: vaeringjar@land
#+DESCRIPTION: UNIX-like Shell Scripting Best Practices
#+KEYWORDS: shell bash

* About

I plan on copying or referring to this file for best practices. I
admit I might break my own rules and if so, that's a bug!

* Executables

- Use -rwxr-xr-x or 755 file permissions.
- Do not give it the file an extension because they do nothing at
  runtime. If you really need one, then append .sh.
- Scope all input args and use local for variables in functions.
- Use the example shebang and options below to fail fast to reduce
  side effect propagation at runtime.
- Only use uppercase letters in global/env variables, for example
  ZZ_VARIABLE. Otherwise, use lowercase, for example zz_other.

* Example bash file

In the code example below we have the shebang with explicit options
set after. Variations exist, but when in doubt use the following.
After that, the example has two functions and the single main function
call that passes the args in, thus creating the initial scope.
Variable inside the functions use local to keep scope. Each variable
handles input differently.

- foo has a custom error prompt
- bar uses the builtin error prompt
- glu can continue with no value assigned.

#+BEGIN_SRC bash
#!/usr/bin/env bash
set -e
set -u
set -o pipefail

example-script_usage() {
    echo Usage: $1 $2
    exit 1
}

example-script_main() {
    local foo=${1:?`example-script_usage $0 foo`}
    local bar=${2:?}
    local glu=${3:-}
    echo "foo: ${foo}"
    echo "bar: ${bar}"
    echo "glu: ${glu}"
}

example-script_main "$@"
#+END_SRC

* War stories

- Using multiple shells for the same project from the same developer
  for no apparent reason. Sometimes I can see using more than one for
  wetware opinions; Alice wants bash and Bob does not. For example,
  Dragora uses [[https://notabug.org/dragora/dragora/src/master/recipes/shells][both mksh and bash]]. But the same developer should just
  pick one, unless they have a good reason.
- Projects that name scripts with the .bsh extension. Don't do this
  because it ambiguously means either bash (bourne again), bourne (the
  predecessor), or bean shell.
- Projects that for example use .sh, .shell, .bash, and no extension
  to differentiate between the use case for the files such as crons,
  startup scripts, interactive scripts, etc.
- Bash scripts that write their own version of getopts.

* See also

- [[https://www.youtube.com/watch?v=yD2ekOEP9sU][The Functional Tao of Bash - Garrett Smith]] - If you only ever watch
  one video on how to write a good shell script.
- [[https://mywiki.wooledge.org/BashPitfalls][Wooledge BashPitfalls Guide]]
