Processes and the terminal
@lune/process describes the running script and starts other programs. @lune/stdio writes to the
terminal, reads from standard input and prompts the user.
local process = require("@lune/process")
local result = process.exec("git", { "rev-parse", "--short", "HEAD" })if result.ok then print("commit", result.stdout)else process.exit(result.code)endArguments, environment and platform
Section titled “Arguments, environment and platform”luneblox run deploy.luau staging --dry-runlocal process = require("@lune/process")
print(process.args[1]) --> stagingprint(process.args[2]) --> --dry-runprocess.args holds the arguments given after the script name. It is read-only: assigning to it
throws.
process.env is a table of environment variables. An unset variable reads as nil. Assigning a
string sets the variable, and assigning nil removes it.
local port = tonumber(process.env.PORT) or 8080
process.env.BUILD_MODE = "release"process.env.BUILD_MODE = nil
for name, value in process.env do print(name, value)end| Value | Meaning |
|---|---|
process.cwd |
The directory the script was started from. It always ends with a path separator. |
process.os |
"linux", "macos" or "windows" |
process.arch |
"x86_64" or "aarch64" |
process.endianness |
"big" or "little" |
Because process.cwd ends with a separator, join onto it directly: process.cwd .. "build".
Exiting
Section titled “Exiting”process.exit(code) stops the script as soon as possible. Code 0, or no code, is success; anything
else is a failure. It overrides the exit code the script would otherwise end with.
if #process.args == 0 then print("usage: deploy <target>") process.exit(1)endRunning a program and waiting for it
Section titled “Running a program and waiting for it”process.exec(program, params, options) runs a program, waits for it to exit, and returns its
result. It yields the calling thread only; other tasks keep running.
local process = require("@lune/process")
local result = process.exec("ls", { "-a" })print(result.ok) --> true when the exit code was zeroprint(result.code)print(result.stdout)print(result.stderr)Options
Section titled “Options”| Option | Meaning |
|---|---|
cwd |
The working directory for the child. A leading ~ is replaced with the user’s home directory. |
env |
Extra environment variables for the child, as a table of strings. |
shell |
true to run through the default shell, or the name of a shell to use. |
stdio |
What to do with the child’s output, and what to feed its input. See below. |
Without shell, the program is started directly and each entry in params is passed as one
argument, with no quoting or expansion. With shell, the line is interpreted by the shell, so
variables, pipes and redirections work.
local result = process.exec("echo $HOME | wc -c", nil, { shell = true })
local built = process.exec("cargo", { "build", "--release" }, { cwd = "crates/app", env = { RUSTFLAGS = "-D warnings" },})The default shell is /bin/sh on Linux and macOS and PowerShell on Windows. Pass a name, such as
shell = "bash", when the command needs a specific one.
Output and input
Section titled “Output and input”stdio is either one kind for both output streams, or a table with stdout, stderr and stdin.
| Kind | Result table | The terminal |
|---|---|---|
"default" |
Receives the output | Sees nothing |
"inherit" |
Receives the output | Sees it as well |
"forward" |
Stays empty | Sees it |
"none" |
Stays empty | Sees nothing; no stream is created |
-- Show a long build's output live, and still inspect it afterwardslocal result = process.exec("cargo", { "test" }, { stdio = "inherit" })
-- Keep stdout for parsing, let errors go straight to the terminallocal listing = process.exec("git", { "status", "--porcelain" }, { stdio = { stdout = "default", stderr = "forward" },})stdin is not a kind: it is a string or buffer written to the child’s standard input. When it is
left out, the child’s input is closed, so a program that reads its input does not hang.
local sorted = process.exec("sort", {}, { stdio = { stdin = "pear\napple\nfig\n" },})print(sorted.stdout)Running a program in the background
Section titled “Running a program in the background”process.create(program, params, options) starts a program and returns at once, with handles to its
streams. Use it for long-running programs and for ones you talk to while they run. Its options are
cwd, env and shell; there is no stdio option.
local process = require("@lune/process")
local child = process.create("cat")
child.stdin:write("hello, world")child.stdin:close()
print(child.stdout:readToEnd()) --> hello, world
local status = child:status()print(status.ok, status.code)| Member | Meaning |
|---|---|
child.stdin:write(data) |
Writes a string or buffer to the child’s input. |
child.stdin:close() |
Closes the child’s input. Many programs wait for this before they finish. |
child.stdout:read(chunkSize) |
Reads up to chunkSize bytes, 1 KB by default. Yields until data arrives; returns nil when there is no more. |
child.stdout:readToEnd() |
Yields until the child exits and returns everything it wrote. |
child.stderr |
A reader with the same two methods. |
child:status() |
Yields until the child exits and returns { ok, code }. |
child:kill() |
Kills the child. |
To stream output as it is produced, read in a loop until nil:
local process = require("@lune/process")local stdio = require("@lune/stdio")
local child = process.create("ping", { "-c", "3", "example.com" })
while true do local chunk = child.stdout:read() if chunk == nil then break end stdio.write(chunk)end
local status = child:status()After child:kill(), output written before the kill can still be read, and writing to stdin
throws.
Writing to the terminal
Section titled “Writing to the terminal”print formats its arguments and adds a newline. stdio.write writes a string to standard output
exactly as given, and stdio.ewrite does the same to standard error.
local stdio = require("@lune/stdio")
stdio.write("Working")for _ = 1, 3 do stdio.write(".")endstdio.write(" done\n")
stdio.ewrite("warning: nothing to do\n")stdio.format(...) returns the string print would have written: arguments separated by a space,
and tables expanded with sorted keys. Use it to build messages for stdio.ewrite or error.
stdio.ewrite(stdio.format("bad config:", { retries = -1 }) .. "\n")Colors and styles
Section titled “Colors and styles”stdio.color(name) and stdio.style(name) return ANSI escape strings. They do not write anything:
write the returned string, and the setting stays in effect until you write a reset.
local stdio = require("@lune/stdio")
local red = stdio.color("red")local bold = stdio.style("bold")local reset = stdio.color("reset") .. stdio.style("reset")
print(`{bold}{red}error:{reset} something went wrong`)Colors are black, red, green, yellow, blue, purple, cyan, white and reset. Styles
are bold, dim and reset. Any other name throws.
Reading input
Section titled “Reading input”stdio.readLine() reads one line from standard input, and stdio.readToEnd() reads everything up
to the end of input. Both work with piped input, which makes them the right choice for scripts run
by other programs.
cat data.json | luneblox run summarize.luaulocal serde = require("@lune/serde")local stdio = require("@lune/stdio")
local data = serde.decode("json", stdio.readToEnd())print(#data, "entries")Prompting the user
Section titled “Prompting the user”stdio.prompt(kind, message, defaultOrOptions) asks an interactive question.
local stdio = require("@lune/stdio")
local name = stdio.prompt("text", "Project name", "my-project")local confirmed = stdio.prompt("confirm", "Create it?", true)local choice = stdio.prompt("select", "Template", { "game", "plugin", "library" })local extras = stdio.prompt("multiselect", "Extras", { "tests", "ci", "docs" })| Kind | Third argument | Returns |
|---|---|---|
"text" |
A default string, optional | The string entered |
"confirm" |
A default boolean, optional | true or false |
"select" |
The list of options | The index of the chosen option, or nil |
"multiselect" |
The list of options | A list of chosen indexes, or nil |
select and multiselect return indexes into the list, not the option strings. Calling
stdio.prompt() with no arguments is a text prompt with no message.
local process = require("@lune/process")local stdio = require("@lune/stdio")
local target = process.args[1] or process.env.DEPLOY_TARGETif target == nil then target = stdio.prompt("text", "Deploy target")endReference
Section titled “Reference”The full listings are on the process reference and the stdio reference. Passing arguments on the command line is covered in running scripts.
