Module Unix.Proc

Running Subprocesses

Running Subprocesses

These functions allow you to run a subprocess, possibly writing data to its standard input, and optionally reading back its standard output and / or standard error.

See the Tutorial.

Functions to Run Subprocesses

Note that all these functions flush stdout and stderr to avoid duplicative output.

val runfull : ?env:string array -> ?usepath:bool -> ?err:(Stdlib.in_channel -> 'a) -> ?writer:(Stdlib.out_channel -> 'b) -> ?reader:(Stdlib.in_channel -> 'c) -> string list -> process_status * 'c option * 'a option

(runfull ?env ?usepath ?err ?writer ?reader argv) runs the program designated by (hd argv) with (tl argv) as its command-line arguments.

argv is a raw argument vector; it is not passed to the shell, so its values do not need to be quoted, and there is no support for shell metacharacters such as I/O redirection or pipes. (See Shell for simple-minded pipeline support.)

If ~writer is provided, it is called to write data to the process's standard input.

If ~reader is provided, it is called to read data from the process's standard output.

If ~err is provided, it is called to read data from the process's standard error.

The return value is a triple consisting of:

  • the process_status
  • the stdout value returned by ~reader, if any, or None if ~reader was not provided
  • the stderr value returned by ~err, if any, or None if ~err was not provided

If given, ~env is used as the environment; the default is to use the value of (environment ()); ~env must be in the same format.

~usepath determines whether or not (hd argv) is searched in PATH; default: true

If (argv = []), Invalid_argument is raised.

Note that if the process produces output to either stdout or stderr, and you don't read it (via ~reader or ~err), the process status will be (WSIGNALED (-8)), indicating SIGPIPE.

val run : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> ?err:(Stdlib.in_channel -> 'a) -> ?writer:(Stdlib.out_channel -> 'b) -> ?reader:(Stdlib.in_channel -> 'c) -> string list -> 'c option * 'a option

run is equivalent to runfull except it returns only the stdout and stderr values.

It does not return the process status.

If the process exits with a non-zero WEXITED status, Failure is raised, unless ~oknon0:true (default: false).

Failure is also raised if the process was killed or stopped by a signal.

Reading from Processes

val readwith : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> (Stdlib.in_channel -> 'a) -> string list -> 'a

readwith is equivalent to run except it returns only the stdout value.

val read : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string

read is (readwith ~reader:Prelude.read).

val readline : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string

readline ?oknon0 ?env ?usepath argv is specially designed to read one line from stdin.

It reads only one line, and ignores any SIGPIPE from the process.

Example: readline ["emacs"; "--version"] would return something like "GNU Emacs 28.2" without the additional lines explaining the copyright and lack of warranty.

val readlines : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string list

readlines is (readwith ~reader:Prelude.readlines).

Writing to Processes

val write : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string -> unit

(write ?oknon0 ?env ?usepath argv str) writes str to the stdin of the subprocess.

All optional parameters are as for run.

This is useful for programs that read stdin and produce no output. Example:

write ["mail"; "-s"; "Your mail is here"; "luser@example.com"] msg

sends mail to the user; msg would be a string containing an RFC-822 email message, complete with headers and body.

val writelines : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string list -> unit

(writelines argv) is (String.concat "\n" $ write argv).

All optional parameters are as for run.

Bidirectional I/O

val rw : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string -> string

(rw ?oknon0 ?env ?usepath argv str) writes str to the process specified by argv and returns the process's stdout.

All optional parameters are as for run.

Example: rw ["tr"; "a-z"; "A-Z"] "ocaml" is an expensive way to get the value "OCAML".

val rwlines : ?oknon0:bool -> ?env:string array -> ?usepath:bool -> string list -> string list -> string

(rwlines ?oknon0 ?env ?usepath argv lines) writes the lines to the process specified by argv and returns the process's output.

All optional parameters are as for run.

Helper Functions

Reader Functions

Suitable for ~reader and ~err.

val reader : ?bufsize:int -> Stdlib.in_channel -> string

(reader chan) is Prelude.read and thus returns all the text from chan.

reader is suitable for a ~reader parameter; because of the pun, when using a local open this shorthand:

  • Unix.Proc.(runfull ~reader ["date"])

is equivalent to:

  • Unix.Proc.(runfull ~reader:reader ["date"])

and thus also to:

  • Unix.Proc.runfull ~reader:Prelude.read ["date"]
val err : ?bufsize:int -> Stdlib.in_channel -> string

(err chan) is Prelude.read and thus returns all the text from chan.

This function is useful for local opens as for reader above.

val sink : Stdlib.in_channel -> unit

(sink chan) reads all data from chan and discards it.

This function is suitable for use with ~reader or ~err in order to avoid a SIGPIPE signal.

Writer Functions

Suitable for ~writer.

val string : string -> Stdlib.out_channel -> unit

(string str chan) writes str to the process via chan.

val lines : string list -> Stdlib.out_channel -> unit

(lines list chan) writes each element of list to the process via chan, adding a terminating newline to each element.

val channel : Stdlib.in_channel -> Stdlib.out_channel -> unit

(channel ic oc) writes the data on the input channel ic to the process via oc.

val file : string -> Stdlib.out_channel -> unit

(file fn oc) writes the contents of the file fn to the process via oc.

Return-value Helpers

val status : (process_status * 'a * 'b) -> int

status converts the triple return value of runfull to the exit status, i.e. the value n of process status (WEXITED n).

If the process status is either WSIGNALED or WSTOPPED, Failure is raised.

val explanation : (process_status * 'a * string option) -> string * string

explanation returns a pair of explanations for the termination of the proces.

The first explanation is short, the second may be longer (possibly multiple lines).

val successful : (process_status * 'a * 'b) -> bool

successful converts the triple return value of runfull to true if it contains WEXITED 0 and false otherwise.

val stdout : ('a * 'b option * 'c) -> 'b

stdout converts the triple return value of runfull to the stdout value.

val stderr : ('a * 'b * 'c option) -> 'c

stderr converts the triple return value of runfull to the stderr value.

Tutorial

Reading Data from a Process

The most common use case is probably to read data back from the process's stdout; you would typically use one of read, readline, or readlines.

read returns all of the process's stdout as a string:

Sometimes you know the subprocess will only return a single line; in this case, you might use readline to save you from having to trim a trailing newline:

readline can also be used to simulate piping the process's stdout to head -1:

readlines returns all of the process's stdout as a list of lines:

Writing Data to a Process

Sometimes you only need to write data to a process; we can send an email with write:

writelines works analogously:

Reading and Writing to and from a Process

Sometimes you need to both write data to a process and then read data back from it. You can do this with rw and rwlines:

Space Efficiency with run

In order to avoid reading all of a potentially large amount of data into memory, you can do all your processing inside ~reader:

Accessing Standard Error with run

Sometimes you want to see the stderr output of a process as well the stdout, perhaps so that you can display it when there's an error:

 match run ~oknon0:true ~err:Prelude.read ["ocamlc";"-c";"foo.ml"] with
| None, Some ""  -> print "compilation successful"
| None, Some err -> print err 

Accessing the Process State with runfull

runfull is just like run but also returns the process state, so you can examine the exit status or detect that the process was killed by a signal.

The status function will extract just the exit status:

A Warning About SIGPIPE

Whenever you interact with a process that produces output to stdout or stderr, if you don't read all of that output, you will get a SIGPIPE exception. For example, /usr/share/dict/words is 1,185,564 bytes on my system; this command looks like a reasonable way to get the first line of the file:

Unix.Proc.run ~reader:readline ["cat"; "/usr/share/dict/words"] 

but in fact it raises SIGPIPE, because we neglected to read the rest of the output.

To solve this problem, you need to either read all the output (which may be inefficient), or ignore the SIGPIPE.

The readline function takes care of ignoring SIGPIPE for you.