Skip to content

Latest commit

 

History

History
81 lines (61 loc) · 4.35 KB

process.md

File metadata and controls

81 lines (61 loc) · 4.35 KB

⚙️ process

Run commands and read the process environment. The function exec runs off the main loop and returns a promise. The environment and working-directory accessors are synchronous.

  • process.exec(command, options?) → A promise resolving to a table {stdout, stderr, code}. The command runs through the platform shell — /bin/sh -c on POSIX and cmd.exe /c on Windows — so shell features like pipes and redirection work with each shell's own syntax. Both streams are captured binary-safe up to a combined 64 MiB cap, past which the child is killed so an endless producer cannot exhaust host memory. The field code is the exit status. A child killed by a POSIX signal reports 128 + signal (a cap overrun is 137), while a terminated Windows process reports its own termination code.

  • The option options.timeoutMs bounds how long the command may run. When it passes the deadline the child is killed and the promise rejects rather than resolving with a partial result, so an overrun can never be mistaken for a normal exit. Without it the call waits as long as the child lives, matching child_process.exec in Node and subprocess.run in Python. A negative value is refused at the call.

    Each exec occupies one thread of the I/O pool for its whole run, and that pool also serves fs and the HTTP client. A command that may hang — anything reaching the network, or a tool that can wait on a lock or a prompt — should carry a timeoutMs, or enough of them will starve the pool the rest of the runtime depends on.

    local result, err = process.exec("curl -s https://example.com", { timeoutMs = 5000 }):await()
    if err then
        -- The command overran its deadline and was killed
    end
  • process.env → A table mapping each environment variable name to its value, captured when the module is required.

  • process.getenv(name, default?) → The value of name, or default (or nil) when it is unset.

  • process.cwd() → The current working directory as a string.

  • process.argv → An array of the script arguments. This is the same data as the global arg table, normalized to a 1-based array that drops arg[0] (the script path) and the host options, leaving just the arguments your script received.

  • process.available → Holds true when this build can run commands, false on the stub builds (iOS, tvOS, watchOS, visionOS, and WebAssembly) where process.exec rejects with "not available in this build".

Commands are not sandboxed. Confine untrusted input before passing it here. A command containing a null byte is rejected rather than silently truncated. On builds without process support (such as the browser build) process.exec rejects with "not available in this build".

Examples

Exec and the environment

-- Runs a command and prints its capture, environment, and working directory.
local async = require("async")
local process = require("process")

async.run(function()
    local result, err = process.exec("echo varn && echo oops 1>&2"):await()
    assert(not err, err)

    print("code:   " .. result.code)
    print("stdout: " .. result.stdout)
    print("stderr: " .. result.stderr)

    print("home:   " .. process.getenv("HOME", "(unset)"))
    print("cwd:    " .. process.cwd())

    if #process.argv > 0 then
        print("argv:   " .. table.concat(process.argv, " "))
    end
end)

Exit status and argv

-- Runs a successful and a failing command and prints env, cwd, and argv.
local async = require("async")
local process = require("process")

if not process.available then
    print("process not available")
    return
end

async.run(function()
    local ok = process.exec("echo hello"):await()
    print("ok code:   " .. ok.code)
    print("ok stdout: " .. ok.stdout)

    local fail = process.exec("exit 3"):await()
    print("fail code: " .. fail.code)

    print("path:      " .. process.getenv("PATH", "(unset)"))
    print("missing:   " .. process.getenv("VARN_NOPE", "(default)"))
    print("env type:  " .. type(process.env))
    print("cwd:       " .. process.cwd())
    print("argv:      " .. table.concat(process.argv, " "))

    print("process status and argv ok")
end)

Under the hood

On native builds commands run through fork + execl("/bin/sh", ...) with pipes for stdout and stderr, capturing each stream until end of file. The browser build uses a stub driver that rejects exec.