{"id":50503126,"url":"https://github.com/jim-collier/bash5-marmot","last_synced_at":"2026-06-02T13:04:09.479Z","repository":{"id":304586418,"uuid":"1019227230","full_name":"jim-collier/bash5-marmot","owner":"jim-collier","description":"A modular Bash framework that takes full advantage of v5 features. Mostly free of expensive forks, subshells, and pipes. Has native functions that optionally fill in for external tools like grep, sed, tr, etc. - for faster performance especially in long-running loops.","archived":false,"fork":false,"pushed_at":"2026-05-26T23:04:56.000Z","size":1789,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T00:19:28.614Z","etag":null,"topics":["bash","library","modular","template"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jim-collier.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":"contributing.md","funding":null,"license":"license.md","code_of_conduct":"code_of_conduct.md","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}},"created_at":"2025-07-14T02:17:08.000Z","updated_at":"2026-05-26T22:54:58.000Z","dependencies_parsed_at":"2025-07-14T04:57:49.586Z","dependency_job_id":"b9a808be-f5a1-4dca-bcd1-a8061a556c7a","html_url":"https://github.com/jim-collier/bash5-marmot","commit_stats":null,"previous_names":["jim-collier/x9bash5-template","jim-collier/bash5-marmot"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/jim-collier/bash5-marmot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jim-collier%2Fbash5-marmot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jim-collier%2Fbash5-marmot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jim-collier%2Fbash5-marmot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jim-collier%2Fbash5-marmot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jim-collier","download_url":"https://codeload.github.com/jim-collier/bash5-marmot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jim-collier%2Fbash5-marmot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33822823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"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","library","modular","template"],"created_at":"2026-06-02T13:04:08.405Z","updated_at":"2026-06-02T13:04:09.470Z","avatar_url":"https://github.com/jim-collier.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD007 -- Unordered list indentation --\u003e\n\u003c!-- markdownlint-disable MD010 -- No hard tabs --\u003e\n\u003c!-- markdownlint-disable MD033 -- No inline html --\u003e\n\u003c!-- markdownlint-disable MD055 -- Table pipe style [Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe] --\u003e\n\u003c!-- markdownlint-disable MD041 -- First line in a file should be a top-level heading --\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![!#/bin/bash](https://img.shields.io/badge/-%23!%2Fbin%2Fbash-1f425f.svg?logo=gnu-bash)](https://www.gnu.org/software/bash/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n![Lifecycle: Stable](https://img.shields.io/badge/Lifecycle-Stable-brightgreen)\n![Support](https://img.shields.io/badge/Support-Maintained-brightgreen)\n![Coverage](https://img.shields.io/badge/Coverage-75%25-yellow)\n![Status: Passing](https://img.shields.io/badge/Status-Passing-brightgreen)\n\n\u003c/div\u003e\n\u003c!--\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)\n![Lifecycle: Alpha](https://img.shields.io/badge/Lifecycle-Alpha-orange)\n![Lifecycle: Beta](https://img.shields.io/badge/Lifecycle-Beta-yellow)\n![Lifecycle: RC](https://img.shields.io/badge/Lifecycle-RC-blue)\n![Lifecycle: Stable](https://img.shields.io/badge/Lifecycle-Stable-brightgreen)\n![Lifecycle: Deprecated](https://img.shields.io/badge/Lifecycle-Deprecated-red)\n![Status: Deprecated](https://img.shields.io/badge/Status-Deprecated-orange)\n![Status: Archived](https://img.shields.io/badge/Status-Archived-lightgrey)\n![Lifecycle: EOL](https://img.shields.io/badge/Lifecycle-EOL-lightgrey)\n![Coverage](https://img.shields.io/badge/Coverage-25%25-red)\n![Coverage](https://img.shields.io/badge/Coverage-50%25-orange)\n![Coverage](https://img.shields.io/badge/Coverage-75%25-yellow)\n![Coverage](https://img.shields.io/badge/Coverage-90%25-brightgreen)\n![Status: Passing](https://img.shields.io/badge/Status-Passing-brightgreen)\n![Status: Failing](https://img.shields.io/badge/Status-Failing-red)\n--\u003e\n\n\u003c!-- TOC ignore:true --\u003e\n# Bash5 Modular Marmot\n\n\u003ctable style=\"border: none; border-collapse: collapse;\"\u003e\n\t\u003ctr style=\"border: none; border-collapse: collapse;\"\u003e\n\t\t\u003ctd style=\"border: none; border-collapse: collapse;\"\u003e\u003cimg src=\"https://github.com/jim-collier/bash5-marmot/blob/main/assets/mascot3.png?raw=true\" alt=\"Modular Marmot\" width=\"320\"/\u003e\u003c/td\u003e\n\t\t\u003ctd style=\"border: none;\"\u003eWelcome to Bash5 Modular Marmot. It's a modular Bash framework that takes full advantage of v5 features. Mostly free of expensive forks, subshells, and pipes. Has native functions that optionally fill in for external tools like grep, sed, tr, etc. - for faster performance especially in long-running loops.\u003c/td\u003e\n\t\u003c/tr style=\"border: none; border-collapse: collapse;\"\u003e\n\u003c/table\u003e\n\n\u003c!-- TOC ignore:true --\u003e\n## Table of contents\n\n\u003c!-- TOC --\u003e\n\n- [Features](#features)\n- [Why Bash 5 and not earlier for compatibility](#why-bash-5-and-not-earlier-for-compatibility)\n\t- [BSD](#bsd)\n\t- [macOS](#macos)\n\t- [POSIX](#posix)\n\t\t- [POSIX was 34 years old at the start of this project](#posix-was-34-years-old-at-the-start-of-this-project)\n\t\t- [POSIX compliance is a crippling tradeoff and often artificial constraint](#posix-compliance-is-a-crippling-tradeoff-and-often-artificial-constraint)\n- [Why Bash at all and not Python or a compiled language](#why-bash-at-all-and-not-python-or-a-compiled-language)\n- [Installation](#installation)\n\t- [Install to unprivileged per-user location](#install-to-unprivileged-per-user-location)\n\t- [Install system-wide](#install-system-wide)\n- [Using it](#using-it)\n- [Coding style](#coding-style)\n- [Third-party script security](#third-party-script-security)\n- [Copyright and license](#copyright-and-license)\n\n\u003c!-- /TOC --\u003e\n\n## Features\n\n| Feature | Advantage and/or benefit | Drawback\n| :--     | :--                      | :--\n|  |  |\n\n## Why Bash 5 and not earlier for compatibility\n\nBash 5 has been widely available by default on all modern Linux distros since 2020.\n\nPrior to v5, Bash 4.3 - which is mostly compatible with Bash 5 - has shipped with most Linux distros since 2015. That's 11 years ago as of 2026.\n\nTherefore, by targeting a Bash version earlier than 5, implies some intention or external requirement other than \"Linux compatibility\".\n\nIf you are running Linux, and aren't writing simple system init scripts that require `sh` POSIX compatibility, there are few to no good arguments to *not* target Bash 5.\n\n### BSD\n\nBy default, BSD doesn't ship with Bash at all. If you want your Bash script to be able to run on all BSDs without requiring the user install even a single dependency, that is usually going to *very* tall order for a script author - save for the most trivial of scripts. Because:\n\n- For full coverage of all the BSDs, you would necessarily not be writing Bash at all, but 100% POSIX compliant `sh` script. (E.g. via Ash, Dash, or something else symlinked to `/bin/sh`.) This would *severely* hamstrings a non-trivial project, and tie it down to 1992. (See the section on POSIX below.)\n\n- Every major core GNU utility of Linux is different on the BSDs (since GPLv3), some rewritten from scratch - and usually not as powerful - or at best with subtly different interfaces that must be individually detected, understood by the script author, and accounted for.\n\n\t- Writing Bash-native alternatives to many core utilities, that work on any platform, is possible in Bash v5. (For example, Bash `=~` with `${BASH_REMATCH[n]}` is shockingly good replacement for `grep -E`. And `val=\"${val//'search'/'replace'}\"` is a strong replacement for `sed s/search/replace/`. Etc.)\n\n\t\tBut such native replacements are generally not possible with any reasonable coding or performance efficiency, in pure POSIX. (POSIX is technically Touring-Complete, so in theory anything is \"possible\". But \"sane\", or \"will complete before the last proton decays\", is another question.)\n\n\t- It's usually much easier, safer, and reasonable to simply require the user to install - via their native BSD package manager, a finite list of updated GNU core utilities. (Which by default install with a \"g\" prefix to avoid conflicting with existing system scripts that assume BSD variants.)\n\n\t\tBut as long as you're doing that, you might as well have the user also install Bash. (Which by default will be GNU Bash v5, the one you want.) Or possibly *only* install Bash. It is usually not an unreasonable expectation that software has prior installation dependencies, especially when so trivially easily met and that has no package or system conflicts.\n\nWith Bash 5 installed, BSD users don't need to change their default shell - that's what script shebang lines are for, running scripts under the correct interpreter.\n\n\u003e Below is an example FreeBSD command to install Bash 5, from the native package manager and standard repository. You could offer to execute this in your script as a user convenience, if you've found it to be necessary - and if elevated privileges fits with your script's permission model:\n\n~~~bash\ndoas pkg install bash\n~~~\n\n### macOS\n\nMacOS uses Zsh as the default shell, only because Bash switched to a license incompatible with Darwin, after Bash v3.2. (GPL 2 ➙ GPL 3.) Apple still gives Bash 3.2 security updates, but functionally it's frozen at v3.2 from 2006. That is absolutely ancient. (20 years old as of 2026.)\n\nTo target v3.2 just for \"macOS compatibility\", is to hamstring a project unnecessarily - at which point you might as well just stick to POSIX.\n\nBut it's easy (and legal) for users to upgrade Bash on macOS to v5 with `ports` or `brew`, and is usually completely reasonable for a script to require it. \"Installing dependencies\" have been part of installing software, since there was software.\n\nWith `brew`, Bash can be installed without administrator rights. And most serious terminal users on macOS have already already done this, even if they prefer Zsh, Fish, Nushell, etc. - if for nothing else, the ability to run third-party Bash \u003e3.2 scripts.\n\nEither way, if a user can install and execute your script, they've already demonstrated the necessary \"skills\" required to copy and paste a single one-liner command to install Bash 5. (And any other updated GNU tools your script might rely on, and version-check.)\n\n\u003e Below is an example one-liner to install Bash 5 on macOS without root access, that you could write into your script as a user convenience feature, if the tested current version isn't high enough:\n\n~~~bash\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"; brew install bash\n~~~\n\nThis installs Bash 5 at the user-level in a way that bash scripts run by the user from the terminal, will default to v5 (except for scripts hard-coded to use `#!/bin/bash`.) v5 is backwards-compatible with v3.2, so existing scripts won't break (except for rare edge-cases involving undefined behavior).\n\nThere are other ways to install Bash 5, including at the system level (e.g. via `macports`), and in ways that preempt Bash 3.2 in most or all user-facing contexts.\n\nScripts and system utilities that hardcode `#!/bin/bash` will still invoke 3.2; SIP prevents replacing or removing the v3.2 there. But that's arguably a good thing - as you can be confident that existing system scripts won't break, and anything specifically written for macOS with hard-coded `#!/bin/bash` will be absolutely guaranteed to behave exactly the same as before Bash 5 was installed.\n\nAnd again - macOS users don't need to change their default shell from Zsh, nor run Bash themselves in the terminal, in order to run Bash 5 scripts from any shell environment.\n\nIf you want to absolutely guarantee that your script runs under bash 5 on macOS (or BSD), no matter how unknown your target environment is: with just a few lines of guard code, your script can first execute under *any* version of bash (even `#!/bin/bash`), test if it's v5, find v5 if its not (or even safely install it fairly isolated), and re-execute itself under that v5. (Or just punt and warn the user if v5 is not installed, and abort.) Any decent LLM can write this code snippet for you (I tried it to make sure - including the safe automatic install), and it would surely be better than torturing yourself with POSIX.\n\nWhich brings us to...\n\n### POSIX\n\n#### POSIX was 34 years old at the start of this project\n\nThat's a year older than the Windows NT CMD \"language\" (extended from DOS BAT), and almost as crusty.\n\nThe POSIX standard was settled in 1992, but mostly dates to 1988 with ksh88 and the original UNIX Bourne shell.\n\nThe Bourne shell itself was introduced in 1979 - in between OG \"Star Wars\", and \"The Empire Stikes Back\". The IBM PC didn't even exist yet.\n\nWe're over a quarter of the way through the current century. POSIX comes from about 10% back into the *previous century*.\n\nIf POSIX were a person, she could be a *grandparent* by now, legally in every country and US state along the way.\n\n#### POSIX compliance is a crippling tradeoff and often artificial constraint\n\nPOSIX has no support for some of the most *basic* entry-level features, protections, and syntax sugar of programming and shell-scripting from this century - such arrays, local variables, built-in arithmetic comparison ergonomics (e.g. `((variable \u003e= 1))`), C-style semantics, and many other basic features.\n\nSome Linux, BSD, and macOS boot processes still use POSIX compatibility for some init scripts, that haven't been retooled in decades because they still work fine.\n\nSuch init scripts, including custom ones you write and call from pluggable locations, generally need to be very simple in scope (and can't exit with \u003e0), and so aren't a good candidate for this template and module library anyway. (Unless forked in a way that can't abort or block the main init thread.)\n\nWhile there may be some other obscure edge-cases, the *only* good reason to absolutely kneecap a project with \"POSIX compliance\", is usually if you are specifically targeting the system init process.\n\n\u003e **It might be helpful to try to get clear with yourself on whether or not you agree with this opinion**:\u003cbr /\u003e\u003cbr /\u003e*The idea that \"I want my script to run on any macOS/BSD system without extra steps\" is not an external constraint. POSIX compliance is a choice, but it adds complexity and may underestimate users' ability to handle simple dependency installations, which they've already demonstrated to get your script.*\n\n## Why Bash at all and not Python or a compiled language\n\nThat's a good, valid, and very useful question that isn't up to this author to answer for you.\n\nBut hopefully, the problem you're trying to solve is in a clear enough problem domain, that the answer is obvious. (Or the scope at least significantly narrowed-down.)\n\nI've written a deeper dive into this question - a [System shell script language comparison](https://github.com/jim-collier/bash5-marmot/blob/main/shell_script_comparison.md), in this same repo, to try to help address the pros and cons of other solutions - including Python-based solutions, and compiled languages.\n\nAnd this document section from another project - \"TOOBLIN: True Object-Oriented Bash\": ['But no really...Why?'](https://github.com/jim-collier/tooblin#but-no-reallywhy) - dives into great detail on the advantages of Bash over other options, for certain problem domains. (Notably system shell scripting.)\n\nThere's also an insidious shibboleth that goes something like, \"After 100 lines use Python not Bash.\" But such cultural thought-stopping bromides have no meaningful or useful value in real-life, for practical domain-specific problem-solving. It's also an ironic cliche, since for system shell scripting specifically, Python almost always requires a few *multiples* of lines of code to do the same as in Bash, for the problems Bash was designed to solve. (Because those aren't the problems Python was designed to solve. The other way around would be equally true.) The problem domains where one is more appropriate than the other, are often obvious and stark - and have nothing to do with code line count. Furthermore, there are numerous high-quality open-source projects written in Bash that span hundreds of modular files, tens of thousands of lines of code, and dozens to hundreds of active contributors. (Specific examples are listed in the same file linked above, in the section \"[Myths and realities of Bash](https://github.com/jim-collier/tooblin#myths-and-realities-of-bash)\".)\n\nPython is certainly a superior choice to Bash in many problem spaces - possibly even for one or more of those examples listed. (Bash seems like an odd choice in at least one case.) But it's not generally true for system shell scripting - reasonably independent of scope, scale, or complexity. (However the Python-based Xonsh shell may fill in some - but not all - of those gaps. See the comparison link above for that specifically.)\n\n## Installation\n\nYou don't *have* to run the installer script, you can just download the files from the latest stable release and use them. In a nutshell:\n\n- Download `bash5-marmot`. It can go anywhere.\n\n\t- Specific renamed implementation copies would ideally go somewhere in your `$PATH`, with the `+x` execute attribute set.\n\n- Download `include/n8_module_*` files, put them somewhere such as an `include` or `lib` directory next to your renamed template script, or under a common Linux place like `/usr/local/lib/n8` or `~/.local/bin/lib`.\n\n\t- *They don't have to go specifically there. The module loader works hard to quickly find them in common \"module\", \"library\", or \"include\" -type locations; either in or under common Linux FSH locations, or relative to the loading script. It doesn't have to be in the `$PATH`, though the loader can also locate them there.*\n\nBut if you'd like an automated install, run the installer script.\n\n\u003e ⚠️ *Before running one of the `curl` commands below, be sure you inspect the `install.bash` script to make sure it's safe.*\n\nBy default (which can be changed by downloading and editing the installer before running), the installer script installs the following files to the following locations:\n\n| File(s)                  | Per-user install location   | System-wide install location\n| :--                      | :--                         | :--\n| bash5-marmot  | ~/.local/**bin**            | /usr/local/**bin**\n| include/n8mod_*          | ~/.local/**bin/lib**/n8     | /usr/local/**lib**/n8\n\n### Install to unprivileged per-user location\n\nAs a general security \"best-practice\" (and for no other reason), this is usually the recommended way.\n\n~~~bash\ncurl -fsSL https://raw.githubusercontent.com/jim-collier/bash5-marmot/main/install.bash | bash\n~~~\n\n### Install system-wide\n\nThe only difference from above, is the `sudo` in front of the `bash` - so that the installer can copy the main template to the system folders noted in the table above. It will auto-detect that it's running as root and will choose those locations.\n\nThis is a convenient long-term approach even for user-level scripts, as the template can remain \"immutable\" (and copy/renamed to a user directory `$PATH` location for specific implementations), and the read-only modules can just remain in the system location.\n\n~~~bash\ncurl -fsSL https://raw.githubusercontent.com/jim-collier/bash5-marmot/main/install.bash | sudo bash\n~~~\n\n## Using it\n\nLook in the function `fMain()` inside `bash5-marmot`, for examples.\n\n## Coding style\n\nHere is the author's [Bash 5 Ultimate Guide](https://github.com/jim-collier/bash-5-ultimate-guide/blob/main/bash-5-ultimate-guide.md).\n\nThis template helps you follow those best-practices.\n\n## Third-party script security\n\nAs with any third-party scripts downloaded from the internet, including this: before executing anything for the first time, it's generally a really good idea to review it for malicious and/or accidental vulnerabilities. You may have your own routine for doing so - but if not, here is an incomplete list of some common things to visually scan and/or explicitly search for, and evaluate:\n\n- Network access. E.g. `wget`, `curl`, `ssh`, `sftp`, `rsync`, `nc`, `ncat`, `socat`, `/dev/udp`, `/dev/tcp`, `openssl`, `iptables`, `nftables`, etc.\n- Disc access. E.g. `cp`, `rsync`, `\u003e`, `\u003e\u003e`, `tee`, `setuid`, `setgid`, etc.\n- Raw device access. E.g. `/dev/`\n- System service calls. E.g. `systemd-`, `systemctl`, `service`, `/etc/init.d` locations, etc.\n- Use of `sudo`, `su`, or `pkexec` (etc.)\n- Arbitrary code execution. E.g. via `eval`, `exec`, etc. with untrusted/unverifiable user input.\n- Vulnerabilities to command-line injection, subshells without quoted variables, etc.\n- Insecure creation of temp files.\n- Calls to _any_ other interpreted language - such as `python`, `php`, etc.\n- Calls to powerful core utilities with complex and/or obfuscated input. E.g. `awk`, `find`, etc.\n- Creating hidden 'dot' files, especially buried in official-looking but unrelated paths.\n- Setting execute bits, e.g. via `chmod`.\n- Obfuscated payloads, e.g. use of `base64`, `rot13`, etc.\n- Binary blobs in the script. E.g. in base64 encoded text.\n- Hardcoded IPs or domains. Especially if obfustaced, e.g. via complex concatenation and/or regex.\n- Hiding data e.g. via `xattr`.\n\nIf any such patterns are found in functions that don't have names that suggest such a thing, and/or aren't well-commented to explain a clear purpose with a clear command use that is easy to follow - be wary. The existence of any of these isn't itself necessarily a problem. (For example, `n8mod_filesys_v1` and `n8mod_logging_v1` contain fairly obvious, clear, and documented disk access patterns.) But if it's not crystal clear exactly why and how it's being done, even to non-expert eyes - consider permanently deleting the script from your system.\n\n## Copyright and license\n\n\u003e Copyright © 2026 Jim Collier (ID: 1cv◂‡Vᛦ)\u003cbr /\u003e\n\u003e Licensed under [The MIT License (MIT)](https://mit-license.org/). No warranty.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjim-collier%2Fbash5-marmot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjim-collier%2Fbash5-marmot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjim-collier%2Fbash5-marmot/lists"}