Shell script

Directory layout

For builds that involve independent command-line tools, use Make, and follow DataMade’s Making Data Guidelines and Clark Grubb’s Makefile Style Guide. Examples: standard_profile_template

If a repository has scripts to set itself up and/or update itself, follow GitHub’s Scripts to Rule Them All. Examples: deploy, standard_profile_template

Filename conventions

bash and sh scripts should use the .sh extension, unless they are in a script/ directory.

Shell options

Start a Bash script with set -euo pipefail. If the script explicitly handles unset variables, omit -u. To see which command failed due to the -e option, add -x.

Reference: The Set Builtin

Code style

Check shell scripts using shellcheck.

Style shell scripts using shfmt: for example, shfmt -w -i 4 -sr (shfmt -f .).

Use:

  • [ ] instead of test

  • [ ] instead of [[ ]], unless required

  • $NAME instead of ${NAME}, unless followed by a word character

  • Subshells to temporarily change directory, for example:

    (
       cd subdir/
       mv x.txt y.txt
    )
    

    Instead of:

    cd subdir/
    mv x.txt y.txt
    cd ..  # AVOID
    

Avoid:

  • set -x in scripts run by continuous integration, because it will expand any secret variables

Continuous integration

Create a .github/workflows/shell.yml file with:

name: Lint Shell
on: [push, pull_request]
jobs:
  build:
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: |
          sudo apt update
          sudo apt install shellcheck shfmt
      - run: shellcheck $(shfmt -f .)
      - run: shfmt -d -i 4 -sr $(shfmt -f .)

Reference