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 `__, for example: `standard_profile_template `__ If a repository has scripts to set itself up and/or update itself, follow GitHub's `Scripts to Rule Them All `__, for example: `deploy `__ and `standard_profile_template `__ Filename conventions ~~~~~~~~~~~~~~~~~~~~ ``sh`` and ``bash`` scripts should use the ``.sh`` extension, unless they are in a ``script/`` directory. Shell options ------------- Start shell scripts 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``. .. seealso:: `The Set Builtin `__ Code style ---------- Check ``/bin/sh`` scripts using `checkbashisms `__. Check shell scripts using `shellcheck `__. Style shell scripts using `shfmt `__: for example, ``shfmt -w -i 4 -sr (shfmt -f .)``. Use: - ``sh`` instead of ``bash``, where possible. Bash is needed for: - `Shell Parameter Expansion `__ (``//``, ``##``, ``%%``, etc.) - `Process Substitution `__ (``<()``) - `The mapfile Builtin `__ - `The pipefail Option `__ - ``[ ]`` instead of ``test`` - ``[ ]`` instead of ``[[ ]]`` in Bash, `unless required `__ - ``$NAME`` instead of ``${NAME}``, unless followed by a word character - Subshells to temporarily change directory, for example: .. code-block:: bash ( cd subdir/ mv x.txt y.txt ) Instead of: .. code-block:: bash cd subdir/ mv x.txt y.txt cd .. # AVOID And: - Avoid ``set -x`` in scripts run by continuous integration, because it will expand any secret variables .. _shell-ci: Continuous integration ---------------------- Create a ``.github/workflows/shell.yml`` file. As a base, use: .. literalinclude:: samples/shell.yml :language: yaml .. tip:: In most cases, you can reuse the `shell `__ workflow. For example: .. code-block:: yaml jobs: lint: uses: open-contracting/.github/.github/workflows/shell.yml@main permissions: contents: read with: ignore: file.sh Maintenance ~~~~~~~~~~~ Find repositories with shell scripts but without ``shell.yml`` files: .. code-block:: bash find . \( -path '*/script/*' -o -name '*.sh' \) ! -path '*/.mypy_cache/*' ! -path '*/node_modules/*' ! -path '*/vendor/*' -exec bash -c 'if [[ -z $(find $(echo {} | cut -d/ -f2) -name shell.yml) ]]; then echo {}; fi' \; Reference --------- - `Shell Command Language `__ - `Bash Conditional Constructs `__ - `Wizard Zines Bite Size series `__