Shell Language Idioms

These are like Oil Language Idioms, but the advice also applies to other Unix shells.

Table of Contents
Style
Prefer test to [
Use Statically Parsed Language Constructs
test Should Only Have 2 or 3 Arguments
Prefer Shell Functions to Aliases
Prefer $'\n' to echo -e
How to Fix Code That strict_errexit Disallows
The local d=$(date %x) Problem
Variations With readonly and export
The if myfunc Problem

Style

Prefer test to [

Idiomatic Oil code doesn't use "puns".

No:

[ -d /tmp ]

Yes:

test -d /tmp

The simple_test_builtin option enforces this.

Use Statically Parsed Language Constructs

Static parsing is one of the syntactic concepts. It leads to better error messages, earlier error messages, and lets tools understand your code.

test Should Only Have 2 or 3 Arguments

In POSIX, the test builtin has a lot of unnecessary flexibility, which leads to bugs.

See Problems With the test Builtin: What Does -a Mean?

No:

test ! -d /tmp
test -d /tmp -a -d /tmp/foo

Yes:

! test -d /tmp
test -d /tmp && test -d /tmp/foo

The simple_test_builtin option enforces that test receives 3 or fewer arguments.

Prefer Shell Functions to Aliases

Functions subsume all the common uses of alias, and can be parsed statically.

No:

alias ll='ls -l'    

Yes:

ll() {         # Shell Style
  ls -l "$@"
}

proc ll {      # Oil Style
  ls -l @ARGV
}

If you're wrapping an external command with a function of the same, use the command builtin:

proc ls {
  command ls --color @ARGV
}

Prefer $'\n' to echo -e

No:

echo -e '\n'   # arg to -e is dynamically parsed

Yes:

echo $'\n'     # statically parsed

How to Fix Code That strict_errexit Disallows

The strict_errexit feature warns you when you would lose errors in shell code.

The local d=$(date %x) Problem

No:

local d=$(date %x)   # ignores failure

Yes:

local d
d=$(date %x)         # fails properly

Better Oil style:

var d = $(date %x)   # fails properly

Variations With readonly and export

In these cases, the builtin comes after the assignment.

No:

readonly d1=$(date %x)
export d2=$(date %x)

Yes:

d1=$(date %x)
readonly d1

d2=$(date %x)
export d2

The if myfunc Problem

No:

if myfunc; then
  echo 'Success'
fi

Shell workaround when the "$@" dispatch pattern is used:

myfunc() {
  echo hi
}

mycaller() {
  if $0 myfunc; then  # $0 starts this script as a new process
    echo 'Success'
  fi
}

"$@"  # invoked like myscript.sh mycaller arg1 arg2 ...

Better Oil Style:

if run myfunc {
  echo 'Success'
}

Generated on Wed Jan 13 20:43:44 PST 2021