{"id":16661175,"url":"https://github.com/sellout/bash-strict-mode","last_synced_at":"2026-05-01T06:33:21.621Z","repository":{"id":137483754,"uuid":"560750666","full_name":"sellout/bash-strict-mode","owner":"sellout","description":"Write better shell scripts","archived":false,"fork":false,"pushed_at":"2026-02-02T18:10:42.000Z","size":169,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-03T08:05:49.814Z","etag":null,"topics":["bash","development","hacktoberfest","nix-flakes"],"latest_commit_sha":null,"homepage":null,"language":"Nix","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sellout.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"sellout","patreon":"sellout"}},"created_at":"2022-11-02T07:19:02.000Z","updated_at":"2025-12-29T04:36:38.000Z","dependencies_parsed_at":"2023-07-19T03:31:31.343Z","dependency_job_id":"9185b77c-0c91-47ba-946d-0687521524f8","html_url":"https://github.com/sellout/bash-strict-mode","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sellout/bash-strict-mode","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellout%2Fbash-strict-mode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellout%2Fbash-strict-mode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellout%2Fbash-strict-mode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellout%2Fbash-strict-mode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sellout","download_url":"https://codeload.github.com/sellout/bash-strict-mode/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellout%2Fbash-strict-mode/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32487462,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","development","hacktoberfest","nix-flakes"],"created_at":"2024-10-12T10:34:02.863Z","updated_at":"2026-05-01T06:33:21.604Z","avatar_url":"https://github.com/sellout.png","language":"Nix","funding_links":["https://github.com/sponsors/sellout","https://patreon.com/sellout"],"categories":[],"sub_categories":[],"readme":"# [Bash](https://www.gnu.org/software/bash/) strict mode\n\n[![built with garnix](https://img.shields.io/endpoint?url=https%3A%2F%2Fgarnix.io%2Fapi%2Fbadges%2Fsellout%2Fbash-strict-mode)](https://garnix.io/repo/sellout/bash-strict-mode)\n\nMaking shell scripts more robust.\n\n## usage\n\nThe simplest usage is with the included Bash wrapper:\n\n```bash\n#!/usr/bin/env strict-bash\n```\n\nThat requires `strict-bash` to be in your `PATH`, but you can also call it directly from the command line:\n\n```bash\n$ path/to/strict-bash some-script.sh\n```\n\nHowever, you can also enable strict mode at the file level by sourcing `strict-mode.bash`. If `strict-mode.bash` is in your `PATH` (as it would be if you use the Nix derivation), then you simply need\n\n```bash\n#!/usr/bin/env bash\nsource strict-mode.bash\n```\n\nOtherwise, you need an extra line to tell your script how to find strict mode. Some common approaches are to find it relative to the script you are currently writing[^1], like:\n\n```bash\n#!/usr/bin/env bash\nSCRIPTDIR=\"$(dirname -- \"$(readlink -f -- \"${BASH_SOURCE[0]}\")\")\"\nsource \"$SCRIPTDIR/../strict-mode.bash\"\n```\n\nor to find it relative to the repository that your scripts are in:\n\n```bash\n#!/usr/bin/env bash\nREPODIR=\"$(git rev-parse --show-toplevel)\"\nsource \"$REPODIR/path/to/strict-mode.bash\"\n```\n\nIf you are using [ShellCheck](https://www.shellcheck.net/) then you should\npass it the `-x` option, which allows you to `source` external scripts. If you\nare also using a non-`PATH` approach, you will likely have to set ShellCheck’s\n[`source-path` directive](https://www.shellcheck.net/wiki/Directive)[^2].\n`source-path`.\n\n### within Nix flakes\n\n```nix\ninputs = {\n  bash-strict-mode = {\n    inputs.nixpkgs.follows = \"nixpkgs\";\n    url = github:sellout/bash-strict-mode;\n  };\n  ...\n};\n```\n\nMake `strict-bash` and `strict-mode.bash` available to your derivations.\n\n```nix\noutputs = {self, bash-strict-mode, flake-utils, nixpkgs, ...}:\n  flake-utils.lib.eachDefaultSystem (system: let\n    pkgs = import nixpkgs {\n      inherit system;\n      overlays = [bash-strict-mode.overlays.default];\n    };\n  in {\n    packages.default = mkDerivation {\n      ...\n      ### Make strict-bash (and strict-mode.bash) available via PATH.\n      nativeBuildInputs = [pkgs.bash-strict-mode];\n    };\n};\n```\n\nUse `strict-bash` instead of `bash` for your derivations’ builds.\n\n```nix\noutputs = {self, bash-strict-mode, flake-utils, nixpkgs, ...}:\n  flake-utils.lib.eachDefaultSystem (system: let\n    pkgs = nixpkgs.legacyPackages.${system}.appendOverlays [\n      bash-strict-mode.overlays.default\n    ];\n  in {\n    ### Apply strict-bash and shellcheck to all the shell snippets in the\n    ### derivation’s build.\n    packages.default = pkgs.checkedDrv (mkDerivation {\n      ...\n    });\n};\n```\n\nUse `strict-bash` locally, from the upstream flake.\n\n```bash\n$ nix run github:sellout/bash-strict-mode someScript\n```\n\n## explanation\n\nThe last line enables strict mode. You could instead just copy the body of `strict-mode.bash` into your script, avoiding the above complexity. But then scripts tend to get out of sync as the contents of this file change, so we recommend against it. By using `source`, all your scripts get checked against a consistent strict mode.\n\nNow, to the behavior of this “strict mode”:\n\n- `set -e` / `-o errexit`: exit immediately if any command has a non-zero exit;\n- `set -u` / `-o nounset`: references to undefined variables are an error;\n- `set -o pipefail`: if a command that’s not the end of the a pipeline (`|`) fails, fail the whole pipeline;\n- `shopt -s inherit_errexit`: If a sub-shell (`$()`) fails, fail the calling shell; and\n- `trap … ERR`: When an error occurs, report _where_ it occurred.\n\n## Often Asked Questions\n\n(FAQ)\n\n\u003c!-- vale Microsoft.FirstPerson = NO --\u003e\n\u003c!-- vale Microsoft.HeadingPunctuation = NO --\u003e\n\n### How do I work around \u003csome failure\u003e?\n\nStrict mode can catch you by surprise, complaining about various idioms that you thought were fine for years. There are some approaches to avoid those issues described in [Use Bash Strict Mode (Unless You Love Debugging)](http://redsymbol.net/articles/unofficial-bash-strict-mode/#issues-and-solutions). One that we recommend is to use a sub-shell to scope disabling parts of strict mode. For example,\n\n```bash\n\u003csafe code\u003e\n(\n  set +o nounset\n  somethingInvolvingUnsetVars\n)\n```\n\nIf the thing you are scoping involves `source` this might not work as well. You have two options:\n\n1. explicitly disable then re-enable the settings\n\n   ```bash\n   set +u\n   source has-unset-vars.sh\n   set -u\n   codeDependingOnSource\n   ```\n\n2. include the code that depends on the `source` in the sub-shell\n\n   ```bash\n   (\n     set +u\n     source has-unset-vars.sh\n     codeDependingOnSource\n   )\n   ```\n\nCombining the two is also an option. This should ensure that the scope holds, while still giving you checking on other code that depends on the environment of the sub-shell.\n\n```bash\n(\n  set +u\n  source has-unset-vars.sh\n  set -u\n  codeDependingOnSource\n)\n```\n\n\u003c!-- vale Microsoft.FirstPerson = YES --\u003e\n\u003c!-- vale Microsoft.HeadingPunctuation = YES --\u003e\n\n## extensions\n\nThere are other useful things to include in a script preface, but they aren’t included here for various reasons:\n\n- `IFS=$'\\n\\t'`: remove space as a field separator – often you want spaces preserved, and only allow newlines or tabs to separate fields. This eliminates a common mistake, but it doesn’t actually make Bash catch any extra bad behavior, so it’s not part of this strict mode.\n- `LC_ALL=C`: remove locale-dependence from the script. This is usually what you want, but if it were part of strict mode, you would lose the value before you could decide you want to keep it, so it’s not included here.\n\n## resources\n\n- [Use Bash Strict Mode (Unless You Love Debugging)](http://redsymbol.net/articles/unofficial-bash-strict-mode/)\n- [Bash strict mode and why you should care](https://olivergondza.github.io/2019/10/01/bash-strict-mode.html)\n- [Fail Fast Bash Scripting](https://dougrichardson.us/notes/fail-fast-bash-scripting.html)\n\n[^1]: Sourcing a file relative to the script is difficult. The second line is a compact way to do it somewhat reliably. See\n\n\u003c!-- vale Microsoft.FirstPerson = NO --\u003e\n\u003c!-- vale Microsoft.Passive = NO --\u003e\n\n    [How do I get the directory where a Bash script is located from within the script itself?](https://stackoverflow.com/questions/59895)\n\n\u003c!-- vale Microsoft.FirstPerson = YES --\u003e\n\u003c!-- vale Microsoft.Passive = YES --\u003e\n\n    for a lot of discussion on this topic.\n\n[^2]: The variable `SCRIPTDIR` above has affinity with the special value that `shellcheck` uses to indicate an import relative to the script (however, the name that you use doesn’t have to match `shellcheck`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsellout%2Fbash-strict-mode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsellout%2Fbash-strict-mode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsellout%2Fbash-strict-mode/lists"}