Cached Exec
cexec runs a command and caches its output for a specific amount of time, so
that re-running the command won't actually run it but instead return the cached
output. Standard output, standard error and the exit code are passed through
unchanged, which lets you drop a caching layer in front of any command in a
shell script.
Prebuilt binaries for Linux, macOS, Windows, FreeBSD, NetBSD and OpenBSD are attached to every release. Building from source needs Zig 0.16 or newer:
zig build -Doptimize=ReleaseSafeThe binary is written to zig-out/bin/cexec.
Run a program and cache its output for 60 seconds, which is the default:
cexec echo Hello WorldRun a program and cache its output for 120 seconds:
cexec -t 120 curl http://localhost:8080Option parsing stops at the first argument that is not an option, so the command keeps its own options:
cexec -t 120 curl -H 'Accept: application/json' http://localhost:8080A lifetime of 0 runs the command every time and refreshes the stored output.
Pass a key with -k and the cache entry is encrypted:
cexec -k hunter2 curl https://api.example.com/privateA key given on the command line is visible to anyone who can list processes.
cexec therefore also reads it from CEXEC_KEY:
CEXEC_KEY=hunter2 cexec curl https://api.example.com/privateReading an entry with the wrong key, or with no key at all, counts as a cache miss. The command runs again and replaces the entry, so that whatever it is that you're building, that depends on the output doesn't break. However, keep in mind that this might lead to exposing the command output in case the key happens to end up being empty.
Entries are stored as one file per command in $XDG_CACHE_HOME/cexec/. When
that variable is unset, cexec falls back to $HOME/.cache/cexec/ on Unix,
$HOME/Library/Caches/cexec/ on macOS and %LOCALAPPDATA%\cexec\ on Windows.
Deleting a file, or the whole directory, removes what was cached.
Everything cexec does is available as a Zig module, so the same caching can be
used from your own program without shelling out:
var result = try cexec.run(gpa, io, .{
.argv = &.{ "curl", "http://localhost:8080" },
.ttl = .fromSeconds(120),
.environ = init.minimal.environ,
});
defer result.deinit(gpa);result.stdout, result.stderr and result.exit_code hold what the command
produced, and result.outcome tells you whether it ran or came from the cache.
Add the dependency with zig fetch --save and import the cexec module.
Copyright © 2026 マリウス
cexec is released under Version 1.1 of the SEGV License, whose full text is included in the LICENSE file. Go read it, there will be a test on it on Monday.
