unflab

unflabunflab

Small, self-contained macOS command-line tools. One download each, no
package manager, and nothing installed that the tool doesn’t actually
need.

curl -fsSL https://unflab.app/get | sh -s -- tree

That installs tree into ~/.local/bin. Several at once works too:

curl -fsSL https://unflab.app/get | sh -s -- wget jq tree

No Homebrew. No Xcode. No runtime dependencies. All packages need to
work on a modern, freshly-installed Mac.

Why

Wanting one small tool and getting a dependency tree is the normal
experience:

  • brew install poppler, to extract text from a PDF, brings cairo,
    glib, nss, nspr, gpgme, fontconfig, freetype, libtiff, openjpeg,
    little-cms2
    and 13 binaries.
  • brew install gnuplot brings the entire Qt stack — qtbase,
    qt5compat, qtsvg — plus cairo, pango, glib, lua and harfbuzz.
  • brew install bloaty, a tool for measuring binary size, brings
    abseil, protobuf, capstone and re2 and requires the whole cmake
    build toolchain.
  • brew install inetutils, because macOS removed ftp and telnet,
    installs about 23 executables.

None of that is Homebrew’s fault — a formula has to serve every build of
a package. But when you want one tool, for one job, it’s a lot of
machinery to acquire and keep updated.

Currently built for Apple Silicon only; as Intel support is deprecated
the build platforms have started to diverge, meaning that they’re becoming
different targets.

What about webi?

webinstall.dev covers a lot of the same
ground: curl … | sh, installs into ~/.local, no package manager, no
root. If you already use it, keep using it — this isn’t an argument that
you picked wrong. webi is great.

The differences are real, though, and they’re mostly about scope:

  • webi downloads, unflab compiles. With one exception: ffmpeg is
    Martin Riedl’s signed static build, which the installer downloads and
    accepts only if it matches a pinned checksum and his Developer ID
    signature, and which goes through the same gate in CI.

  • webi is multi-platform, unflab is macOS-only.

  • Different rosters. webi leans towards web-dev tooling; unflab
    leans towards Unix plumbing.

  • unflab checks what it ships. unflab puts every binary through the
    otool -L gate in CI, whereas webi uses the official binaries.

The upshot is that unflab can provide a specific utility from a larger
package. In fact, that’s the reason for all this: I needed timeout (a.k.a
gtimeout) from GNU coreutils, but didn’t want to install the whole thing.

None of that makes them mutually exclusive, and I’d recommend using webi
for most packages, especially larger ones where you actually want the larger
suite. So it’s packaged here too:

curl -fsSL https://unflab.app/get | sh -s -- webi

…just so you don’t have to additionally look up webinstall.dev
for the command.

What “self-contained” means

macOS has no static libSystem and doesn’t support fully-static
executables, so nothing here claims to be statically linked. What every
binary is checked for, in CI, before release:

it links against nothing outside /usr/lib and /System/.

That’s scripts/verify.sh, and a failure fails the build. Homebrew may
be used on the build machine to get a static library or a tool; the gate
is what proves none of it survived into what you download.

The unflab command

Installing anything also drops a small unflab script beside it, so you
don’t have to find that curl line again:

unflab jq mlr                 # install more
unflab --uninstall ftp        # remove one
unflab --purge doggo          # remove it and its config files
unflab --list                 # see what there is
unflab --info jq              # read a utility's page
unflab unflab                 # update this helper

It is not a package manager and doesn’t want to become one. It keeps no
database, records no state, and updates nothing behind your back — it
re-runs the same one-liner you started with, so you don’t have to retype
it.

Delete it if its presence offends you; nothing depends on it, and

curl -fsSL https://unflab.app/get | sh -s -- <utility>

does everything it does.

Available now

Utility What it does Original Package Version Licence
ant the CLI for the Claude API, plus bonus “askclaude” script ant 1.38.0 MIT
apfel Apple’s on-device language model from the command line apfel 1.16.0 MIT
astyle reformat C, C++, C#, Java and Objective-C source astyle 3.6.19 MIT
bloaty a size profiler for binaries bloaty 1.1 Apache-2.0
btop a resource monitor with a full-colour terminal interface btop 1.4.7 Apache-2.0
cc2md convert Claude Code session logs to readable markdown cc2md 0.1.0 MIT
doggo a modern DNS client for the command line doggo 1.4.0 GPL-3.0-or-later
duti set default apps for file types and URL schemes duti 1.5.4 public-domain AND MIT
envsubst GNU envsubst: substitutes environment variables in shell templates. gettext 1.0 GPL-3.0-or-later
expunge_adobecc kill off all Adobe Creative Cloud jobs expunge_adobecc 2 MIT
expunge_googleupdate kill off all Adobe Creative Cloud jobs expunge_googleupdate 1 MIT
fd a fast, user-friendly alternative to find fd 10.5.0 MIT OR Apache-2.0
ffmpeg convert, record and stream audio and video ffmpeg 9.0.2 GPL-3.0-or-later
ffplay a simple media player built on the FFmpeg libraries ffmpeg 9.0.2 GPL-3.0-or-later
ffprobe inspect media files and streams ffmpeg 9.0.2 GPL-3.0-or-later
ftp The GNU FTP client, from GNU inetutils. inetutils 2.8 GPL-3.0-or-later
fzf command-line fuzzy finder fzf 0.74.4 MIT
glow render markdown on the command line glow 3.0.0 MIT
htop interactive process viewer htop 3.5.3 GPL-2.0-or-later
jq command-line JSON processor jq 1.8.2 MIT
micro a modern and intuitive terminal-based text editor micro 2.0.15 MIT
mlr like awk, sed, cut and join, but for CSV, TSV and JSON miller 6.22.0 BSD-2-Clause
mutool Inspect, convert and manipulate PDF files, from MuPDF. mutool 1.28.5 AGPL-3.0-or-later
mutool-lite The same PDF tool as mutool, at half the size without the per-language CJK fonts. mutool 1.28.5 AGPL-3.0-or-later
n install and switch between Node.js versions n 10.2.0 MIT
nano a small and friendly text editor nano 9.2 GPL-3.0-or-later
pdftotext extract text from PDF files pdftotext 26.10.0 GPL-2.0-or-later
pstree list directories as an indented tree pstree 2.40 GPL-3.0
pv monitor the progress of data through a pipe pv 1.12.0 GPL-3.0-or-later
qpdf structural, content-preserving PDF transformation qpdf 12.4.2 Apache-2.0
rdfind find duplicate files and optionally link or delete them rdfind 1.8.0 GPL-2.0-or-later
rename Perl-powered file rename script with many helpful built-ins rename 1.601 Artistic-1.0-Perl OR GPL-1.0-or-later
rg recursively searches directories for a regex pattern while respecting your gitignore ripgrep 15.2.0 MIT OR UNLICENSE
shfmt format shell scripts shfmt 3.14.1 BSD-3-Clause
socat multipurpose relay for bidirectional data transfer socat 1.8.1.3 GPL-2.0-only
subversion Apache Subversion version control system subversion 1.14.5 Apache-2.0
telnet The GNU telnet client, from GNU inetutils. inetutils 2.8 GPL-3.0-or-later
tmux terminal multiplexer tmux 3.7c ISC
tree list directories as an indented tree tree 2.3.2 GPL-2.0-or-later
typst a markup-based typesetting system typst 0.15.1 Apache-2.0
unflab install and remove unflab utilities by name unflab 1.0.0 MIT
webi the webinstall.dev bootstrap wrapper webi 1.3.2 MPL-2.0
wget retrieve files over HTTP, HTTPS and FTP wget 1.25.0 GPL-3.0-or-later
xmlstarlet query and edit XML from the shell xmlstarlet 1.6.1 MIT
xq pretty-print and query XML and HTML xq 1.5.1 MIT
yq query and edit YAML, JSON, XML, TOML and CSV from the shell yq 4.54.1 MIT

Installed using their own installers

Not built here. get shows the command and asks before running it.

Name What it is
deno JavaScript and TypeScript runtime
mise manage versions of language runtimes and dev tools
node Node.js, installed with a one-off run of n
uv Astral’s Python package and project manager

coreutils (105)

Utility What it does Original Package Version Licence
arch print machine hardware name (same as uname -m) coreutils 9.12 GPL-3.0-or-later
b2sum compute and check BLAKE2 message digest coreutils 9.12 GPL-3.0-or-later
base32 base32 encode/decode data and print to standard output coreutils 9.12 GPL-3.0-or-later
base64 base64 encode/decode data and print to standard output coreutils 9.12 GPL-3.0-or-later
basename strip directory and suffix from filenames coreutils 9.12 GPL-3.0-or-later
basenc Encode/decode data and print to standard output coreutils 9.12 GPL-3.0-or-later
cat concatenate files and print on the standard output coreutils 9.12 GPL-3.0-or-later
chgrp change group ownership coreutils 9.12 GPL-3.0-or-later
chmod change file mode bits coreutils 9.12 GPL-3.0-or-later
chown change file owner and group coreutils 9.12 GPL-3.0-or-later
chroot run command or interactive shell with special root directory coreutils 9.12 GPL-3.0-or-later
cksum compute and verify file checksums coreutils 9.12 GPL-3.0-or-later
comm compare two sorted files line by line coreutils 9.12 GPL-3.0-or-later
cp copy files and directories coreutils 9.12 GPL-3.0-or-later
csplit split a file into sections determined by context lines coreutils 9.12 GPL-3.0-or-later
cut remove sections from each line of files coreutils 9.12 GPL-3.0-or-later
date print or set the system date and time coreutils 9.12 GPL-3.0-or-later
dd convert and copy a file coreutils 9.12 GPL-3.0-or-later
df report file system space usage coreutils 9.12 GPL-3.0-or-later
dir list directory contents coreutils 9.12 GPL-3.0-or-later
dircolors color setup for ls coreutils 9.12 GPL-3.0-or-later
dirname strip last component from file name coreutils 9.12 GPL-3.0-or-later
du estimate file space usage coreutils 9.12 GPL-3.0-or-later
echo display a line of text coreutils 9.12 GPL-3.0-or-later
env run a program in a modified environment coreutils 9.12 GPL-3.0-or-later
expand convert tabs to spaces coreutils 9.12 GPL-3.0-or-later
expr evaluate expressions coreutils 9.12 GPL-3.0-or-later
factor factor numbers coreutils 9.12 GPL-3.0-or-later
false do nothing, unsuccessfully coreutils 9.12 GPL-3.0-or-later
fmt simple optimal text formatter coreutils 9.12 GPL-3.0-or-later
fold wrap each input line to fit in specified width coreutils 9.12 GPL-3.0-or-later
groups print the groups a user is in coreutils 9.12 GPL-3.0-or-later
head output the first part of files coreutils 9.12 GPL-3.0-or-later
hostid print the numeric identifier for the current host coreutils 9.12 GPL-3.0-or-later
hostname set or print the name of the current host system coreutils 9.12 GPL-3.0-or-later
id print real and effective user and group IDs coreutils 9.12 GPL-3.0-or-later
install copy files and set attributes coreutils 9.12 GPL-3.0-or-later
join join lines of two files on a common field coreutils 9.12 GPL-3.0-or-later
kill send signals to processes, or list signals coreutils 9.12 GPL-3.0-or-later
link call the link function to create a link to a file coreutils 9.12 GPL-3.0-or-later
ln make links between files coreutils 9.12 GPL-3.0-or-later
logname print user’s login name coreutils 9.12 GPL-3.0-or-later
ls list directory contents coreutils 9.12 GPL-3.0-or-later
md5sum compute and check MD5 message digest coreutils 9.12 GPL-3.0-or-later
mkdir make directories coreutils 9.12 GPL-3.0-or-later
mkfifo make FIFOs (named pipes) coreutils 9.12 GPL-3.0-or-later
mknod make block or character special files coreutils 9.12 GPL-3.0-or-later
mktemp create a temporary file or directory coreutils 9.12 GPL-3.0-or-later
mv move (rename) files coreutils 9.12 GPL-3.0-or-later
nice run a program with modified scheduling priority coreutils 9.12 GPL-3.0-or-later
nl number lines of files coreutils 9.12 GPL-3.0-or-later
nohup run a command immune to hangups, with output to a non-tty coreutils 9.12 GPL-3.0-or-later
nproc print the number of processing units available coreutils 9.12 GPL-3.0-or-later
numfmt Convert numbers from/to human-readable strings coreutils 9.12 GPL-3.0-or-later
od dump files in octal and other formats coreutils 9.12 GPL-3.0-or-later
paste merge lines of files coreutils 9.12 GPL-3.0-or-later
pathchk check whether file names are valid or portable coreutils 9.12 GPL-3.0-or-later
pinky lightweight finger coreutils 9.12 GPL-3.0-or-later
pr convert text files for printing coreutils 9.12 GPL-3.0-or-later
printenv print all or part of environment coreutils 9.12 GPL-3.0-or-later
printf format and print data coreutils 9.12 GPL-3.0-or-later
ptx produce a permuted index of file contents coreutils 9.12 GPL-3.0-or-later
pwd print name of current/working directory coreutils 9.12 GPL-3.0-or-later
readlink print resolved symbolic links or canonical file names coreutils 9.12 GPL-3.0-or-later
realpath print the resolved path coreutils 9.12 GPL-3.0-or-later
rm remove files or directories coreutils 9.12 GPL-3.0-or-later
rmdir remove empty directories coreutils 9.12 GPL-3.0-or-later
seq print a sequence of numbers coreutils 9.12 GPL-3.0-or-later
sha1sum compute and check SHA1 message digest coreutils 9.12 GPL-3.0-or-later
sha224sum compute and check SHA224 message digest coreutils 9.12 GPL-3.0-or-later
sha256sum compute and check SHA256 message digest coreutils 9.12 GPL-3.0-or-later
sha384sum compute and check SHA384 message digest coreutils 9.12 GPL-3.0-or-later
sha512sum compute and check SHA512 message digest coreutils 9.12 GPL-3.0-or-later
shred overwrite a file to hide its contents, and optionally delete it coreutils 9.12 GPL-3.0-or-later
shuf generate random permutations coreutils 9.12 GPL-3.0-or-later
sleep delay for a specified amount of time coreutils 9.12 GPL-3.0-or-later
sort sort lines of text files coreutils 9.12 GPL-3.0-or-later
split split a file into pieces coreutils 9.12 GPL-3.0-or-later
stat display file or file system status coreutils 9.12 GPL-3.0-or-later
stdbuf Run COMMAND, with modified buffering operations for its standard streams coreutils 9.12 GPL-3.0-or-later
stty change and print terminal line settings coreutils 9.12 GPL-3.0-or-later
sum checksum and count the blocks in a file coreutils 9.12 GPL-3.0-or-later
sync Synchronize cached writes to persistent storage coreutils 9.12 GPL-3.0-or-later
tac concatenate and print files in reverse coreutils 9.12 GPL-3.0-or-later
tail output the last part of files coreutils 9.12 GPL-3.0-or-later
tee read from standard input and write to standard output and files coreutils 9.12 GPL-3.0-or-later
test check file types and compare values coreutils 9.12 GPL-3.0-or-later
timeout run a command with a time limit coreutils 9.12 GPL-3.0-or-later
touch create file, or change file timestamps coreutils 9.12 GPL-3.0-or-later
tr translate or delete characters coreutils 9.12 GPL-3.0-or-later
true do nothing, successfully coreutils 9.12 GPL-3.0-or-later
truncate shrink or extend the size of a file to the specified size coreutils 9.12 GPL-3.0-or-later
tsort perform topological sort coreutils 9.12 GPL-3.0-or-later
tty print the file name of the terminal connected to standard input coreutils 9.12 GPL-3.0-or-later
uname print system information coreutils 9.12 GPL-3.0-or-later
unexpand convert spaces to tabs coreutils 9.12 GPL-3.0-or-later
uniq report or omit repeated lines coreutils 9.12 GPL-3.0-or-later
unlink call the unlink function to remove the specified file coreutils 9.12 GPL-3.0-or-later
uptime tell how long the system has been running coreutils 9.12 GPL-3.0-or-later
users print the user names of users currently logged in to the current host coreutils 9.12 GPL-3.0-or-later
vdir list directory contents coreutils 9.12 GPL-3.0-or-later
wc print newline, word, and byte counts for each file coreutils 9.12 GPL-3.0-or-later
who show who is logged on coreutils 9.12 GPL-3.0-or-later
whoami print effective user name coreutils 9.12 GPL-3.0-or-later
yes output a string repeatedly until killed coreutils 9.12 GPL-3.0-or-later

Not in unflab

Asked for often enough to say why not, and where to get them instead.

Name What it is
bun JavaScript runtime, bundler and package manager
class-dump
cmake cross-platform build system generator
dig DNS lookup utility from BIND
gettext-tools
ghostscript PostScript and PDF interpreter
mtr traceroute and ping combined
nmap network scanner, with ncat
tomlq jq for TOML, from the Python yq package

Installing manually

Each package ships its own install.sh. The curl | sh line above runs
it for you; a manual download runs the same script, the same way:

tar xzf unflab-tree-2.3.2-arm64-apple-darwin.tar.gz
./install.sh                        # into ~/.local/bin
./install.sh --prefix /usr/local/bin
./install.sh --uninstall            # remove it
./install.sh --purge                # and its config files

It won’t touch your shell config without asking, it won’t overwrite a
command you already have, and it won’t remove a file it didn’t install.

Building it yourself

The intention is that you just download the GitHub CI binaries. After
all, keeping your Mac clean of all that build nonsense is one of the
primary goals… GitHub’s CI runners have all the tools necessary to
build these utilities so you don’t have to.

However, if you want to use this repo for your own builds; add to it (yes
please); or are just suspicious (fair enough), then:

make                  # list the recipes
make packages         # list every installable package
make wget             # what you can do with one
make wget.prereqs     # what building it needs
make wget.build       # fetch, verify, build, gate
make wget.install     # install what you just built
make wget.package     # make a release archive

Targets take either a package name or a recipe name, which matters where
one recipe builds several packages:

make ftp.install         # just ftp
make inetutils.install   # everything that recipe builds (ftp, telnet)
make coreutils.install   # the whole suite, if you really want it
make timeout.install     # builds coreutils, installs only timeout

Recipes live in utils/‹name›/: recipe.sh pins the upstream source and
its SHA-256, and manifest.tsv says what gets installed where. Building
without a pinned checksum is refused.

make ‹util›.prereqs will tell you what’s missing, but won’t install a
large toolchain behind your back — needing one to build a 50KB binary
would rather defeat the point.

Licence

This repository’s own code — the recipes, the installer, the workflows —
is MIT (see LICENSE).

Every utility it builds stays under its own upstream licence, shipped
verbatim inside the package. unflab compiles and packages other
people’s software; it doesn’t relicense it. Each recipe records the exact
source URL, version and checksum it was built from — or, for a binary it
installs rather than builds, the URL and checksum of that binary.