,,                 ,,    ,,  ,,
           `7MM               `7MM  `7MM *MM
             MM                 MM    MM  MM
    ,pP"Ybd  MMpMMMb.  .gP"Ya   MM    MM  MM,dMMb.   ,6"Yb.  ,pP"Ybd  .gP"Ya
    8I   `"  MM    MM ,M'   Yb  MM    MM  MM    `Mb 8)   MM  8I   `" ,M'   Yb
    `YMMMa.  MM    MM 8M""""""  MM    MM  MM     M8  ,pm9MM  `YMMMa. 8M""""""
    L.   I8  MM    MM YM.    ,  MM    MM  MM.   ,M9 8M   MM  L.   I8 YM.    ,
    M9mmmP'.JMML  JMML.`Mbmmd'.JMML..JMML.P^YbmdP'  `Moo9^Yo.M9mmmP'  `Mbmmd'

text bsd text lnx test mac hits of code release license

About

The shellbase framework serves as a foundation for Unix shell scripts. It follows POSIX, the Portable Operating System Interface standard, and runs on any Unix-like system. The framework provides logging, validation helpers, signal handling, garbage collection, and support for concurrent instances.

Global variables carry the BASE_ prefix. Clients may use them and should place temporary files under $BASE_WIP. Functions with the base_ prefix stay internal. Clients shouldn’t call them.

See dotfiles, gento, pulse, and toolbox for example projects built on the framework.

Install

The framework ships as a single non-executable POSIX shell script, base.sh. Install the file from the repository:

git clone https://github.com/rdavid/shellbase.git &&
  ./shellbase/app/install

Or install the file from a release. Some operating systems demand administrative rights to write to /usr/local/bin. Prefix tar with sudo or doas when needed:

REL=0.9.20260707
SRC=https://github.com/rdavid/shellbase/archive/refs/tags/v$REL.tar.gz
curl --location --silent $SRC |
  tar \
    --directory /usr/local/bin \
    --extract \
    --gzip \
    --strip-components=2 \
    shellbase-$REL/lib/base.sh

Using

Verify /usr/local/bin appears in your PATH. Then source the framework from your script:

#!/bin/sh
# File not following:
#  shellcheck disable=SC1091
. base.sh
log I\'m using the shellbase.

The framework provides three loggers: log for information, loge for errors, and logw for warnings. Each logger prints to standard error. In non-interactive mode, each also appends the message to the log file under a single lowercase level tag: i for information, e for errors, and w for warnings.

Try shellbase without installation:

#!/bin/sh
REL=0.9.20260707
SRC=https://github.com/rdavid/shellbase/archive/refs/tags/v$REL.tar.gz
eval "$(
  curl --location --silent $SRC |
    tar \
      --extract \
      --gzip \
      --to-stdout \
      shellbase-$REL/lib/base.sh
)"
log I\'m using the shellbase.

prettytable example:

. base.sh
{
  printf 'ID\tNAME\tTITLE\n'
  printf '123456789\tJohn Foo\tDirector\n'
  printf '12\tMike Bar\tEngineer\n'
} | prettytable

Output:

+-----------+----------+----------+
|ID         |NAME      |TITLE     |
+-----------+----------+----------+
|123456789  |John Foo  |Director  |
|12         |Mike Bar  |Engineer  |
+-----------+----------+----------+

Test

The project uses Daniel J. Bernstein’s build system, redo. You can install Sergey Matveev’s goredo implementation. redo lint applies the following linters to the source files: actionlint, checkmake, hadolint, reuse, shellcheck, shfmt, typos, vale, yamllint, zizmor. redo test runs the unit tests against each installed shell. redo test-container repeats the tests inside the supported container images. The project uses David Rabkin's goredoer to build goredo.

A run of redo lint shows the conventions in practice: commands echo before they run, missing linters stay silent instead of warning, a failure doesn’t stop the rest, and the script’s only line of stdout is the bare OK that redo reads as the target.

lint.do — redo lint
1 $ redo lint
2 20260727-14:02:01 » dash -n ./lint.do
20260727-14:02:01 » mksh -n ./lint.do
20260727-14:02:02 » shellcheck ./*.do ./app/* ./lib/*
3 actionlint, checkmake, hadolint — not installed, nothing logged
4 20260727-14:02:04 shfmt failed, err=1.
20260727-14:02:05 » yamllint ./.github/*.yml ./.github/workflows/*.yml
5 OK

What the numbers point at

  1. 1

    No shellbase-specific CLI to learn — redo lint is a plain redo target running a POSIX .do script.

  2. 2

    cmd_run echoes the exact command it's about to run, in the same green as every other info-level log line, before any output appears.

  3. 3

    cmd_runif skips missing tools without a warning. Silence is the signal that a linter isn't installed, not a yellow line asking you to fix it.

  4. 4

    A genuine failure, printed in red by loge. One bad linter doesn't stop the rest — lint.do keeps going.

  5. 5

    The only line on stdout. redo reads it as the target's content; every line above it went to stderr.

License

shellbase belongs to David Rabkin and uses a Zero-Clause BSD license.