unflab
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 gnuplotbrings 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 removedftpandtelnet,
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 -Lgate 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/liband/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.