Update on wisp, the Linux shell that swaps bash syntax for Lua. Since that post, I found and fixed a batch of bugs that only showed up under a real pty, not in the unit test suite, and added a few things people asked for.
The core idea is simple: any global function you define in ~/.config/wisp/init.lua becomes a shell command. No alias syntax, no config DSL -- just Lua.
function ll(input)
return sh("ls -la")
end
function g(input, pattern)
return sh("grep " .. pattern)
end
Then use them like any command: wisp> ll | g "wisp"
Why Lua?
Every shell invents its own scripting language. Bash has its own grammar, fish has its own, zsh has its own. They all have different syntax for aliases, functions, conditionals, and loops.
Lua is already a real programming language with closures, tables, loops, and a standard library. It's the most embedded scripting language on the planet. Why invent another one?
Structured Pipeline Data
The other thing that's different: native pipeline stages (user-defined functions and builtins) pass Lua tables between each other in-process. No fork, no serialization.
function nums(input)
return { {n = 3}, {n = 1}, {n = 2} }
end
function sorted(input)
table.sort(input, function(a, b) return a.n < b.n end)
return input
end
wisp> nums | sorted passes the table directly from nums to sorted without forking or text parsing. Only when a pipeline hits an external command (grep, wc, anything on $PATH) does wisp fork and serialize to bytes.
What's in v3
- Full job control: fg, bg, kill, disown, wait, jobs -l
- Globbing, brace expansion
-
${VAR:-default}expansion -
2>&1redirect - Tab completion for commands, filenames, Lua functions
- Levenshtein command-not-found suggestions
- Script mode:
./wisp -f script.luaor#!/usr/bin/env wisp -
pkg-- a thin front-end to LuaRocks (pkg install luasocket), with installed rocks immediately available viarequire() - Builtins: cd, exit, export, command, jobs, fg, bg, kill, disown, wait, echo, source, pwd, type, pkg
- A round of bug fixes that only showed up under a real pty: command substitution, builtins in pipelines/redirects, Ctrl-C at the prompt, cd/$PWD staleness, and more (see commit history)
Benchmarks
Measured against bash 5.3, zsh 5.9, dash 0.5, and Lua 5.4 itself:
| wisp | bash | zsh | dash | |
|---|---|---|---|---|
| Binary, stripped | 423K | 1.1M | ~900K | ~120K |
| Source lines | 2,533 | ~500K | ~250K | ~20K |
| Startup, best of 40 | 1.6ms | 2.1ms | 1.2ms | 1.2ms |
wisp starts faster than bash and runs at about a third of the binary size, while staying honest about the tradeoff: it pays for luaL_openlibs up front, which is what buys a real language at the prompt instead of sub-millisecond startup. Full methodology and more benchmarks (throughput, peak RSS, pipeline overhead) are in the README.
What it can't do (yet)
No array syntax, no [[ ]] tests, no ${!prefix*} expansions, no heredocs. Not a bash replacement for scripts -- focused on interactive use.
I used AI for help with the C++ I had trouble with, but the design decisions -- the Lua embedding model, the parser structure, the job control -- are mine, and the code has been through real testing and pty-level debugging.
GitHub: https://github.com/Hinikaa/wisp

Top comments (0)