Skip to content

Instantly share code, notes, and snippets.

@mischief
Last active March 21, 2026 04:33
Show Gist options
  • Select an option

  • Save mischief/b734df526b16413f8d80e62fa601a2e8 to your computer and use it in GitHub Desktop.

Select an option

Save mischief/b734df526b16413f8d80e62fa601a2e8 to your computer and use it in GitHub Desktop.
a "shell" in lua
local unistd = require("posix.unistd")
local stdlib = require("posix.stdlib")
local wait = require("posix.sys.wait")
local topcmd = nil
local Cmd = {}
function Cmd:exec()
local cmds = {}
table.insert(cmds, self)
local p = self.pipeto
while p do
table.insert(cmds, p)
p = p.pipeto
end
local pipes = {}
for i = 1, #cmds - 1 do
local p0, p1 = unistd.pipe()
if not p0 then
error(p1)
end
pipes[i] = {p0, p1}
end
local pids = {}
for i = 1, #cmds do
local pid, errmsg = unistd.fork()
if pid == nil then
error(errmsg)
elseif pid == 0 then
-- child
if i > 1 then
-- stdin
local fd, errmsg = unistd.dup2(pipes[i-1][1], 0)
if not fd then
error(errmsg)
end
end
if i < #cmds then
-- stdout
local fd, errmsg = unistd.dup2(pipes[i][2], 1)
if not fd then
error(errmsg)
end
end
for i = 1, #pipes do
unistd.close(pipes[i][1])
unistd.close(pipes[i][2])
end
local ok, err = unistd.execp(cmds[i].cmd, cmds[i].argt)
if not ok then
io.stderr:write(err .. "\n")
unistd._exit(1)
end
else
table.insert(pids, pid)
-- parent
end
end
for i = 1, #pipes do
unistd.close(pipes[i][1])
unistd.close(pipes[i][2])
end
for i = 1, #pids do
local status, errmsg = wait.wait(pids[i])
if status == nil then
error(errmsg)
end
end
return nil
end
function Cmd:__call(...)
self:arg(select(1, ...))
return self
end
function Cmd:__bor(right)
local c = self
if self.parent then
c = self.parent
end
c.pipeto = right
return self
end
function Cmd:__band(what)
-- it's a fake Command, convert it to arg
local v = stdlib.getenv(what.cmd)
if not v then
error("no such environment variable " .. what.cmd)
end
self:arg(v)
-- set parent so we can track pipes correctly
what.parent = self
return self
end
function Cmd:arg(a)
self.argt[#self.argt + 1] = a
return self
end
local function newcmd(c)
return setmetatable({
cmd = c, argt = {}
}, {
__index = Cmd,
__call = Cmd.__call,
__bor = Cmd.__bor,
__band = Cmd.__band,
})
end
local function getenv(arg)
if arg then
print(stdlib.getenv(arg))
else
for k,v in pairs(stdlib.getenv()) do
print(string.format("%s=%s", k, v))
end
end
end
local function setenv(k, v)
stdlib.setenv(k,v)
end
local gbls = {
print = print,
quit = function() os.exit(0) end,
help = function()
print([[Syntax:
help: this help
print("hello")
setenv("X", "Y")
setenv("X")
getenv("X")
cmd "arg1" "arg2"
cmd1 | cmd2
cmd1 "arg" | cmd2 "arg" | cmd3 "arg"
cmd1 &ENVVAR
]])
end,
getenv = getenv,
setenv = setenv,
}
local function gindex(t, k)
if gbls[k] then return gbls[k] end
local cmd = newcmd(k)
if not topcmd then
topcmd = cmd
end
return cmd
end
local env = setmetatable({
prompt = "lsh# ",
}, {__index = gindex})
repeat
io.write(env.prompt)
local line = io.read()
if not line then break end
local chk, err = load("return " .. line, "stdin", "t", env)
if not chk then
print(err)
else
local ok, result = pcall(chk)
if not ok then
print(result)
goto continue
end
if ok and type(result) == "function" and not topcmd then
result()
goto continue
end
if topcmd then
local ok, err = pcall(function() topcmd:exec() end)
end
topcmd = nil
if not ok then
print(err)
end
end
::continue::
until false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment