The os/Process module
The Process module deals with everything related to launching a child process, waiting for its status or signals, and communicating with it.
Basic usage
A process can be launched by providing an array or a list of arguments to the Process
constructor:
import os/Process
p := Process new(["cat", "/etc/hosts"])
exitCode := p execute()
// at this point, the process has ended
// and 'exitCode' contains the value returned by the program
Instead of running execute, one might want to run getOutput
directly, to get
the standard output of the process as a string
p := Process new(["cat", "/etc/hosts"])
out := p getOutput()
// out now contains the contents of the /etc/hosts file
Or getErrOutput to get the standard error output:
p := Process new(["cat", "/etc/hosts"])
err := p getErrOutput()
// err is empty
To retrieve the result of both stderr and stdout, see the ‘Redirecting’ section below.
However, depending on your use case, that might not be the best way to do it.
Manual wait and pid
execute
will start the child process, wait for it to finish, and print the output.
However, we can do things manually if we want:
p := Process new(["curl", "example.org"])
p executeNoWait()
p wait()
The wait
method will wait until the child process has exited or errored. If you
just want to check if a process is still running, waitNoHang
can be used instead:
p := Process new(["curl", "example.org"])
p executeNoWait()
while (p waitNoHang() == -1) {
Time sleepMilli(20)
}
To execute a bunch of processes in parallel, using a JobPool is easier and more suitable.
Process settings
Current working directory
By default, a process will inherit from the current working directory. To make
the child process run in a specified directory, use the setCwd
method
p := Process new(["cat", "hosts"])
p setCwd("/etc")
p execute()
Environment
To specify custom environment variables for a process, use the setEnv
method
with a HashMap<String, String>
:
p := Process new(["bash", "-c", "echo $MYVAR"])
env := HashMap<String, String> new()
env put("MYVAR", "42")
p setEnv(env)
// prints 42
p execute()
Communicating with a process
Redirecting stdin, stdout, stderr
One may use pipes to redirect the standard input, output, or error stream of a process.
import os/[Process, Pipe, PipeReader]
p := Process new(["some", "process"])
(out, err) := (Pipe new(), Pipe new())
p setStdout(out)
p setStderr(err)
exitCode := p execute()
outString := PipeReader new(out) toString()
errString := PipeReader new(err) toString()
out close('r'). close('w')
err close('r'). close('w')
// we now have the exit code in exitCode, the
// stdout in outString, and the stderr in errString
Streaming output
This can be used to stream stdout to the output of our main program, if the launched process is interactive. If blinkenlights is still up and running when you try this, it should display star wars scene in ASCII art:
p := Process new(["nc", "towel.blinkenlights.nl", "23"])
out := Pipe new()
out setNonBlocking()
p setStdout(out)
p executeNoWait()
while (true) {
chr := out read()
if (chr != '\0') {
chr print()
} else if(p waitNoHang() > 0) {
// process is done
break
}
}
Terminate or kill a process
The terminate
method will send a process the SIGTERM
message, while the kill
method
will send a process the SIGKILL
message.
This can be used to gracefully end a process:
p terminate()
if (p waitNoHang() != -1) {
// give a few seconds of grace..
Time sleepSec(3)
}
if (p waitNoHang() != -1) {
// still not finished? alright, that's enough
p kill()
}