A few POSIX shell tips

I was recently working on the release build script for sb and decided I should document a few things I find myself looking up just about every time. One of the advantages of the POSIX shell (not bash, zsh, but Bourne shell, usually /bin/sh on most unix-y systems) is that it has a specification however I'm usually frustrated trying to find the information I need.

I prefer the POSIX shell because it's the default on most unix-y system. If I have to be sure if the shell is installed on the target system (like bash or zsh) I'd rather just use a higher-level scripting language like node or ruby where the same requirement exists.

Arrays

Psych! There aren't any arrays. Bash has arrays but POSIX does not. If I need an array structure I can use a space-separated string (or another delimiter if you've set $IFS) as the data structure, e.g. PLATFORMS="arm64-darwin amd64-linux amd64-darwin". Now I can loop over the variable using a for loop:

PLATFORMS="arm64-darwin amd64-linux amd64-darwin"

for PLATFORM in ${PLATFORMS}; do
  # GOOS=FOO GOARCH=BAR go build ...
done

Or I can take the lack of arrays as a signal I should stop writing a shell script and reach for a language that has the concept of arrays to solve my problem.

Conditional "flags"

I often forget the various flags one can use in a conditional (aka test/if) statement. Here's the documentation and here are a few of my favorites:

  • -n tests if the string is non-zero, e.g.
if [ -n "${UPLOAD_URL}" ]; then
    upload_file "${PLATFORM}"
fi
  • -z the opposite of -n.
  • -f tests if the pathname resolves to a file.

Functions and args

  • Functions must be declared before they are invoked.
  • Functions look like:
someFunc() {
  echo "a func!"
}
  • There is no syntax for functions accepting args instead they are mapped to $n. $@ is all the args.
upload_file() {
    NAME=$1

    zip "${NAME}.zip" "${NAME}"
    curl -H "Accept: application/vnd.github.v3+json" \
         -H "Authorization: Bearer ${GITHUB_TOKEN}" \
         -H "Content-Type: application/zip" \
         --data-binary "@${NAME}.zip" \
         "${UPLOAD_URL}?name=${NAME}.zip"
}

upload_file "${PLATFORM}"

Use shellcheck

This is just preference but I really love using shellcheck to avoid any footguns and enforce a consistent style with my shell scripts. I typically invoke it like this: shellcheck -o all script-name.sh.

Have a comment? Send an email to my public inbox. Please follow proper mail etiquette.