Standard Library
class Process
Runs another program as a child process and gives you its standard input, standard output and standard error as streams.
since 0.1.0-alpha.1linux
Overview
Process(path, args) starts the child right away. path is used exactly as written, so give the full path of the executable (/bin/cat); no search of the PATH is made. The child receives path followed by args as its argument list. Feed it with write and closeStdin, receive its output with onStdout and onStderr, and learn how it ended with exitCode. Everything runs on the event loop, so none of these calls blocks.
Check ok() after constructing. A path that does not exist or cannot be executed still counts as started: the failure shows up later as exit code 127. A process that could not be started at all (an empty path, or the operating system refused to create the pipes) reports false from ok() and also resolves exitCode() with 127.
A program that never calls exitCode() may finish while its children are still running; they are not waited for.
Description
Process(path, args) starts a child program. The child's argument vector is [path] + args.
The path is used exactly as given, with no search of PATH: write /bin/cat, not cat.
(A helper that searches PATH can be written in the language itself, from env::get and a
file-existence check.)
The child's three standard streams are connected to pipes, and the object gives you the stream surface for each of them:
ok()istruewhen the child could be started. A path that does not exist does not makeok()false: the child starts, fails to run the program, and exits with code127.write(text)sends text to the child's standard input. Like a socket send, it queues what the pipe cannot take yet and drains it as the child reads.closeStdin()closes the child's input; for programs such ascatthat read until end-of-input, closing it is how you say "that is everything".onStdout(callback)andonStderr(callback)deliver the child's output in chunks as it arrives. A chunk is whatever was available, not necessarily a whole line.exitCode()returns aPromise<int>that resolves when the child has exited. Waiting for a child never blocks the program: other timers, sockets and tasks keep running. The code is the child's exit status;128plus the signal number if a signal ended the child (the shell convention);127if it never ran.kill()asks the child to terminate (SIGTERM). A pendingexitCode()then resolves with143.
When exitCode() resolves, any output still sitting in the child's pipes has been delivered
first, and every descriptor the object owned has been closed, so starting many short-lived
children does not leak descriptors.
A program that never calls exitCode() sets up no wait for the child and may finish while the
child is still running; the child is not reaped (standard Unix behavior).
Rules
- Linux only. Starting processes is a Linux feature. A program that uses
Processis rejected at compile time when it is built for a Windows target. - The path is explicit; there is no
PATHlookup. - A bad path is reported as exit code
127, not asok() == false.ok()isfalseonly when the child could not be created at all, andexitCode()then also resolves127. - Call
exitCode()once the child should be waited for; its result is cached, so calling it again returns the same promise. - The output callbacks run on the event loop, one at a time, never concurrently with your other code.
kill()sendsSIGTERMto this object's own child and to nothing else.
Examples
Feeding a child through its standard input and waiting for it. cat echoes its input and exits
when the input is closed:
Process cat = Process("/bin/cat", []);
if (!cat.ok()) {
console.writeln("could not start the child");
} else {
cat.onStdout((chunk) => console.writeln("child said: ${chunk}"));
cat.write("hello\n");
cat.closeStdin();
int code = await cat.exitCode();
console.writeln("exit code ${code}");
}
This prints child said: hello and then exit code 0.
Notes
For a child that expects a terminal (a shell, an editor, anything that checks isatty), use
Pty instead. A pseudo-terminal merges standard output and standard error into one stream.
Examples
Running a command and reading its output
Process p = Process("/bin/cat", []);
if (p.ok()) {
p.onStdout((chunk) => console.write(chunk));
p.write("hello\n");
p.closeStdin();
int code = await p.exitCode();
console.writeln("exit code: ${code}");
}
Collecting both output streams
Process p = Process("/bin/ls", ["-l"]);
p.onStdout((chunk) => console.write(chunk));
p.onStderr((chunk) => console.write(chunk));
p.exitCode().then((code) => console.writeln("exit code: ${code}"));
Constructors
new
new(string path, Array<string> args)Starts path as a child process with the given arguments.
The child's argument list is path followed by args. path is not searched for on the PATH. Call ok() to find out whether the child was created; an executable that does not exist is reported through the exit code (127), not through ok().
Parameters
- path
- The full path of the executable to run. An empty path starts nothing.
- args
- The arguments passed to the program, not counting the program name itself.
Fields
pid
int pidThe operating-system process id of the child, or -1 when it could not be started.
Methods
closeStdin
closeStdin() -> voidCloses the child's standard input.
Programs such as cat read until their input ends, so closing it is how you tell them that you are done writing. Calling it more than once is harmless, and so is calling it on a process that was not started.
exitCode
exitCode() -> Promise<int>Returns a promise that resolves with the child's exit code once it has ended.
The code is the value the child passed to its exit call (0 to 255), 128 plus the signal number when a signal ended it, or 127 when the process could not be started or run. Before it resolves, any output still waiting in the pipes is delivered to the output handlers, and then the pipes are closed. Calling exitCode again returns the same promise. Waiting for the child uses a non-blocking wait on the event loop, so it needs no signal handler.
Returns
A promise of the exit code.
See also: kill
kill
kill() -> voidAsks the child to stop by sending it the termination signal (SIGTERM), and closes its standard input.
Does nothing when the process was not started or has already ended. If exitCode is pending it resolves with 143 (128 plus the signal number) unless the child handles the signal itself.
See also: exitCode
ok
ok() -> boolTells whether the child process was created.
A path that does not exist still gives true here, because the failure to run it is only seen by the child; it surfaces as exit code 127 from exitCode. Use this check before attaching handlers.
Returns
true when the child was created, false when nothing was started.
onStderr
onStderr((string) => void cb) -> voidSets the function that receives the child's standard error output.
It works exactly like onStdout, for the error stream. Only call it when ok() is true.
Parameters
- cb
- The function called with each chunk of error output.
See also: onStdout
onStdout
onStdout((string) => void cb) -> voidSets the function that receives the child's standard output.
cb is called with each piece of output as it arrives. A piece is whatever the pipe held at that moment, not necessarily a whole line. Calling onStdout again replaces the previous function. Any output still waiting in the pipe when the child ends is delivered before exitCode resolves.
Only call this when ok() is true. On a process that was not started the registration keeps the event loop alive and the program never ends on its own.
Parameters
- cb
- The function called with each chunk of output.
See also: onStderr
write
write(string s) -> voidSends text to the child's standard input.
The text is queued and written as the pipe has room, so a large string never blocks the program. Writing to a process that was not started, or after closeStdin, does nothing.
Parameters
- s
- The text to send.