racket/pkgs/racket-doc/scribblings/reference/subprocess.scrbl
Matthew Flatt 710320e3dc "Mac OS X" -> "Mac OS"
Although "macOS" is the correct name for Apple's current desktop OS,
we've decided to go with "Mac OS" to cover all of Apple's Unix-like
desktop OS versions. The label "Mac OS" is more readable, clear in
context (i.e., unlikely to be confused with the Mac OSes that
proceeded Mac OS X), and as likely to match Apple's future OS names
as anything.
2016-12-23 12:18:36 -07:00

559 lines
23 KiB
Racket

#lang scribble/doc
@(require "mz.rkt" (for-label racket/system))
@title[#:tag "subprocess"]{Processes}
@defproc*[([(subprocess [stdout (or/c (and/c output-port? file-stream-port?) #f)]
[stdin (or/c (and/c input-port? file-stream-port?) #f)]
[stderr (or/c (and/c output-port? file-stream-port?) #f 'stdout)]
[command path-string?]
[arg (or/c path? string-no-nuls? bytes-no-nuls?)] ...)
(values subprocess?
(or/c (and/c input-port? file-stream-port?) #f)
(or/c (and/c output-port? file-stream-port?) #f)
(or/c (and/c input-port? file-stream-port?) #f))]
[(subprocess [stdout (or/c (and/c output-port? file-stream-port?) #f)]
[stdin (or/c (and/c input-port? file-stream-port?) #f)]
[stderr (or/c (and/c output-port? file-stream-port?) #f)]
[command path-string?]
[exact 'exact]
[arg string?])
(values subprocess?
(or/c (and/c input-port? file-stream-port?) #f)
(or/c (and/c output-port? file-stream-port?) #f)
(or/c (and/c input-port? file-stream-port?) #f))])]{
Creates a new process in the underlying operating system to execute
@racket[command] asynchronously, providing the new process with
environment variables @racket[current-environment-variables]. See also
@racket[system] and @racket[process] from
@racketmodname[racket/system].
@margin-note{On Unix and Mac OS, subprocess creation is separate
from starting the program indicated by @racket[command]. In
particular, if @racket[command] refers to a non-existent or
non-executable file, an error will be reported (via standard error and
a non-0 exit code) in the subprocess, not in the creating
process.}
The @racket[command] argument is a path to a program executable, and
the @racket[arg]s are command-line arguments for the program. See
@racket[find-executable-path] for locating an executable based on
the @envvar{PATH} environment variable. On
Unix and Mac OS, command-line arguments are passed as byte strings,
and string @racket[arg]s are converted using the current locale's
encoding (see @secref["encodings"]). On Windows, command-line
arguments are passed as strings, and bytes strings are converted using
UTF-8.
On Windows, the first @racket[arg] can be replaced with
@indexed-racket['exact], which triggers a Windows-specific behavior:
the sole @racket[arg] is used exactly as the command-line for the
subprocess. Otherwise, on Windows, a command-line string is
constructed from @racket[command] and @racket[arg] so that a typical
Windows console application can parse it back to an array of
arguments. If @racket['exact] is provided on a non-Windows platform,
the @exnraise[exn:fail:contract].
@margin-note{For information on the Windows command-line conventions,
search for ``command line parsing'' at
@tt{http://msdn.microsoft.com/}.}
When provided as a port, @racket[stdout] is used for the launched
process's standard output, @racket[stdin] is used for the process's
standard input, and @racket[stderr] is used for the process's standard
error. All provided ports must be file-stream ports. Any of the ports
can be @racket[#f], in which case a system pipe is created and
returned by @racket[subprocess]. The @racket[stderr] argument can be
@racket['stdout], in which case the same file-stream port or system pipe
that is supplied as standard output is also used for standard error.
For each port or @racket['stdout] that is provided, no
pipe is created and the corresponding returned value is @racket[#f].
The @racket[subprocess] procedure returns four values:
@itemize[
@item{a @deftech{subprocess} value representing the created process;}
@item{an input port piped from the process's standard output, or
@racket[#f] if @racket[stdout-output-port] was a port;}
@item{an output port piped to the process standard input, or
@racket[#f] if @racket[stdin-input-port] was a port;}
@item{an input port piped from the process's standard error, or
@racket[#f] if @racket[stderr-output-port] was a port or @racket['stdout].}
]
@bold{Important:} All ports returned from @racket[subprocess] must be
explicitly closed, usually with @racket[close-input-port] or
@racket[close-output-port].
@margin-note{A @tech{file-stream port} for communicating with a
subprocess is normally a pipe with a limited capacity. Beware of
creating deadlock by serializing a write to a subprocess followed by a
read, while the subprocess does the same, so that both processes end
up blocking on a write because the other end must first read to make
room in the pipe. Beware also of waiting for a subprocess to finish
without reading its output, because the subprocess may be blocked attempting
to write output into a full pipe.}
The returned ports are @tech{file-stream ports} (see
@secref["file-ports"]), and they are placed into the management of
the current custodian (see @secref["custodians"]). The
@exnraise[exn:fail] when a low-level error prevents the spawning of a
process or the creation of operating system pipes for process
communication.
If the @racket[subprocess-group-enabled] parameter's value is true,
then the new process is created as a new OS-level process group. In
that case, @racket[subprocess-kill] attempts to terminate all
processes within the group, which may include additional processes
created by the subprocess. See @racket[subprocess-kill] for details,
and see @racket[subprocess-group-enabled] for additional caveats.
The @racket[current-subprocess-custodian-mode] parameter determines
whether the subprocess itself is registered with the current
@tech{custodian} so that a custodian shutdown calls
@racket[subprocess-kill] for the subprocess.
A subprocess can be used as a @tech{synchronizable event} (see @secref["sync"]).
A subprocess value is @tech{ready for synchronization} when
@racket[subprocess-wait] would not block; @resultItself{subprocess value}.}
@defproc[(subprocess-wait [subproc subprocess?]) void?]{
Blocks until the process represented by @racket[subproc]
terminates. The @racket[subproc] value also can be used with
@racket[sync] and @racket[sync/timeout].}
@defproc[(subprocess-status [subproc subprocess?])
(or/c 'running
exact-nonnegative-integer?)]{
Returns @indexed-racket['running] if the process represented by
@racket[subproc] is still running, or its exit code otherwise. The
exit code is an exact integer, and @racket[0] typically indicates
success. If the process terminated due to a fault or signal, the exit
code is non-zero.}
@defproc[(subprocess-kill [subproc subprocess?] [force? any/c]) void?]{
Terminates the subprocess represented by @racket[subproc]. The precise
action depends on whether @racket[force?] is true, whether the process
was created in its own group by setting the
@racket[subprocess-group-enabled] parameter to a true value, and the
current platform:
@itemlist[
@item{@racket[force?] is true, not a group, all platforms: Terminates
the process if the process still running.}
@item{@racket[force?] is false, not a group, on Unix or Mac OS:
Sends the process an interrupt signal instead of a kill
signal.}
@item{@racket[force?] is false, not a group, on Windows: No action
is taken.}
@item{@racket[force?] is true, a group, on Unix or Mac OS:
Terminates all processes in the group, but only if
@racket[subprocess-status] has never produced a
non-@racket['running] result for the subprocess and only if
functions like @racket[subprocess-wait] and @racket[sync] have
not detected the subprocess's completion. Otherwise, no action
is taken (because the immediate process is known to have
terminated while the continued existence of the group is
unknown).}
@item{@racket[force?] is true, a group, on Windows: Terminates
the process if the process still running.}
@item{@racket[force?] is false, a group, on Unix or Mac OS: The
same as when @racket[force?] is @racket[#t], but when the group
is sent a signal, it is an interrupt signal instead of a kill
signal.}
@item{@racket[force?] is false, a group, on Windows: All processes
in the group receive a CTRL-BREAK signal (independent of
whether the immediate subprocess has terminated).}
]
If an error occurs during termination, the @exnraise[exn:fail].}
@defproc[(subprocess-pid [subproc subprocess?]) exact-nonnegative-integer?]{
Returns the operating system's numerical ID (if any) for the process
represented by @racket[subproc]. The result is valid only as long as
the process is running.}
@defproc[(subprocess? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a subprocess value, @racket[#f]
otherwise.}
@defparam[current-subprocess-custodian-mode mode (or/c #f 'kill 'interrupt)]{
A @tech{parameter} that determines whether a subprocess (as created by
@racket[subprocess] or wrappers like @racket[process]) is registered
with the current @tech{custodian}. If the parameter value is
@racket[#f], then the subprocess is not registered with the
custodian---although any created ports are registered. If the
parameter value is @racket['kill] or @racket['interrupt], then the
subprocess is shut down through @racket[subprocess-kill], where
@racket['kill] supplies a @racket[#t] value for the @racket[_force?]
argument and @racket['interrupt] supplies a @racket[#f] value. The
shutdown may occur either before or after ports created for the
subprocess are closed.
Custodian-triggered shutdown is limited by details of process handling
in the host system. For example, @racket[process] and @racket[system]
may create an intermediate shell process to run a program, in which
case custodian-based termination shuts down the shell process and
probably not the process started by the shell. See also
@racket[subprocess-kill]. Process groups (see
@racket[subprocess-group-enabled]) can address some limitations, but
not all of them.}
@defboolparam[subprocess-group-enabled on?]{
A @tech{parameter} that determines whether a subprocess is created as
a new process group. See @racket[subprocess-kill] for more information.
Beware that creating a group may interfere with the job control in an
interactive shell, since job control is based on process groups.}
@defproc[(shell-execute [verb (or/c string? #f)]
[target string?]
[parameters string?]
[dir path-string?]
[show-mode symbol?])
#f]{
@index['("ShellExecute")]{Performs} the action specified by @racket[verb]
on @racket[target] in Windows. For platforms other than Windows, the
@exnraise[exn:fail:unsupported].
For example,
@racketblock[
(shell-execute #f "http://racket-lang.org" ""
(current-directory) 'sw_shownormal)
]
Opens the Racket home page in a browser window.
The @racket[verb] can be @racket[#f], in which case the operating
system will use a default verb. Common verbs include @racket["open"],
@racket["edit"], @racket["find"], @racket["explore"], and
@racket["print"].
The @racket[target] is the target for the action, usually a filename
path. The file could be executable, or it could be a file with a
recognized extension that can be handled by an installed application.
The @racket[parameters] argument is passed on to the system to perform
the action. For example, in the case of opening an executable, the
@racket[parameters] is used as the command line (after the executable
name).
The @racket[dir] is used as the current directory when performing the
action.
The @racket[show-mode] sets the display mode for a Window affected by
the action. It must be one of the following symbols; the description
of each symbol's meaning is taken from the Windows API documentation.
@itemize[
@item{@indexed-racket['sw_hide] or @indexed-racket['SW_HIDE] ---
Hides the window and activates another window.}
@item{@indexed-racket['sw_maximize] or @indexed-racket['SW_MAXIMIZE]
--- Maximizes the window.}
@item{@indexed-racket['sw_minimize] or @indexed-racket['SW_MINIMIZE]
--- Minimizes the window and activates the next top-level window in
the z-order.}
@item{@indexed-racket['sw_restore] or @indexed-racket['SW_RESTORE]
--- Activates and displays the window. If the window is minimized or
maximized, Windows restores it to its original size and position.}
@item{@indexed-racket['sw_show] or @indexed-racket['SW_SHOW] ---
Activates the window and displays it in its current size and
position.}
@item{@indexed-racket['sw_showdefault] or
@indexed-racket['SW_SHOWDEFAULT] --- Uses a default.}
@item{@indexed-racket['sw_showmaximized] or
@indexed-racket['SW_SHOWMAXIMIZED] --- Activates the window and
displays it as a maximized window.}
@item{@indexed-racket['sw_showminimized] or
@indexed-racket['SW_SHOWMINIMIZED] --- Activates the window and
displays it as a minimized window.}
@item{@indexed-racket['sw_showminnoactive] or
@indexed-racket['SW_SHOWMINNOACTIVE] --- Displays the window as a
minimized window. The active window remains active.}
@item{@indexed-racket['sw_showna] or @indexed-racket['SW_SHOWNA] ---
Displays the window in its current state. The active window remains
active.}
@item{@indexed-racket['sw_shownoactivate] or
@indexed-racket['SW_SHOWNOACTIVATE] --- Displays a window in its most
recent size and position. The active window remains active.}
@item{@indexed-racket['sw_shownormal] or
@indexed-racket['SW_SHOWNORMAL] --- Activates and displays a
window. If the window is minimized or maximized, Windows restores it
to its original size and position.}
]
If the action fails, the @exnraise[exn:fail]. If the action succeeds,
the result is @racket[#f].
In future versions of Racket, the result may be a subprocess value if
the operating system did returns a process handle (but if a subprocess
value is returned, its process ID will be @racket[0] instead of the
real process ID).}
@; ----------------------------------------------------------------------
@section{Simple Subprocesses}
@note-lib[racket/system]
@defproc[(system [command (or/c string-no-nuls? bytes-no-nuls?)]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
boolean?]{
Executes a Unix, Mac OS, or Windows shell command synchronously
(i.e., the call to @racket[system] does not return until the
subprocess has ended). The @racket[command] argument is a string or
byte string containing no nul characters. If the command succeeds, the
return value is @racket[#t], @racket[#f] otherwise.
@margin-note{See also @racket[subprocess] for notes about error
handling and the limited buffer capacity of subprocess pipes.}
If @racket[set-pwd?] is true, then the @envvar{PWD} environment
variable is set to the value of @racket[(current-directory)] when
starting the shell process.
See also @racket[current-subprocess-custodian-mode] and
@racket[subprocess-group-enabled], which affect the subprocess used to
implement @racket[system].
The resulting process writes to @racket[(current-output-port)], reads
from @racket[(current-input-port)], and logs errors to
@racket[(current-error-port)]. To gather the process's non-error
output to a string, for example, use @racket[with-output-to-string],
which sets @racket[current-output-port] while calling the given
function:
@racketblock[
(with-output-to-string (lambda () (system "date")))
]}
@defproc*[([(system* [command path-string?]
[arg (or/c path? string-no-nuls? bytes-no-nuls?)] ...
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
boolean?]
[(system* [command path-string?] [exact 'exact] [arg string?]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
boolean?])]{
Like @racket[system], except that @racket[command] is a filename that
is executed directly (instead of through a shell command; see
@racket[find-executable-path] for locating an executable based on
the @envvar{PATH} environment variable), and the
@racket[arg]s are the arguments. The executed file is passed the
specified string arguments (which must contain no nul
characters).
On Windows, the first argument after @racket[command] can be
@racket['exact], and the final @racket[arg] is a complete command
line. See @racket[subprocess] for details.}
@defproc[(system/exit-code [command (or/c string-no-nuls? bytes-no-nuls?)]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
byte?]{
Like @racket[system], except that the result is the exit code returned
by the subprocess. A @racket[0] result normally indicates success.}
@defproc*[([(system*/exit-code [command path-string?]
[arg (or/c path? string-no-nuls? bytes-no-nuls?)] ...
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
byte?]
[(system*/exit-code [command path-string?]
[exact 'exact] [arg string?]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
byte?])]{
Like @racket[system*], but returns the exit code like
@racket[system/exit-code].}
@defproc[(process [command (or/c string-no-nuls? bytes-no-nuls?)]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
(list input-port?
output-port?
exact-nonnegative-integer?
input-port?
((or/c 'status 'wait 'interrupt 'kill) . -> . any))]{
Executes a shell command asynchronously (using @exec{sh} on Unix
and Mac OS, @exec{cmd} on Windows). The result is a list of five
values:
@margin-note{See also @racket[subprocess] for notes about error
handling and the limited buffer capacity of subprocess pipes.}
@itemize[
@item{an input port piped from the subprocess's standard output,}
@item{an output port piped to the subprocess standard input,}
@item{the system process id of the subprocess,}
@item{an input port piped from the subprocess's standard
error, and}
@item{a procedure of one argument, either @racket['status], @racket['wait],
@racket['interrupt], @racket['exit-code] or @racket['kill]:
@itemize[
@item{@racket['status] returns the status of the subprocess as one
of @racket['running], @racket['done-ok], or
@racket['done-error].}
@item{@racket['exit-code] returns the integer exit code of the
subprocess or @racket[#f] if it is still running.}
@item{@racket['wait] blocks execution in the current thread until
the subprocess has completed.}
@item{@racket['interrupt] sends the subprocess an interrupt signal
on @|AllUnix|, and takes no action on Windows. The result is
@|void-const|.
@margin-note{On Unix and Mac OS, if @racket[command] runs a
single program, then @exec{sh} typically runs the program in
such a way that it replaces @exec{sh} in the same process. For
reliable and precise control over process creation, however, use
@racket[process*].}}
@item{@racket['kill] terminates the subprocess and returns
@|void-const|. Note that the immediate process created by
@racket[process] is a shell process that may run another program;
terminating the shell process may not terminate processes that
the shell starts, particularly on Windows.}
]}
]
@bold{Important:} All three ports returned from @racket[process] must
be explicitly closed with @racket[close-input-port] or
@racket[close-output-port].
If @racket[set-pwd?] is true, then @envvar{PWD} is set in the same way
as @racket[system].
See also @racket[current-subprocess-custodian-mode] and
@racket[subprocess-group-enabled], which affect the subprocess used to
implement @racket[process]. In particular, the @racket['interrupt] and
@racket['kill] process-control messages are implemented via
@racket[subprocess-kill], so they can affect a process group instead
of a single process.}
@defproc*[([(process* [command path-string?]
[arg (or/c path? string-no-nuls? bytes-no-nuls?)] ...
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
list?]
[(process* [command path-string?] [exact 'exact] [arg string?]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
list?])]{
Like @racket[process], except that @racket[command] is a filename that
is executed directly like @racket[system*], and the @racket[arg]s are the arguments. On
Windows, as for @racket[system*], the first @racket[arg] can be
replaced with @racket['exact].}
@defproc[(process/ports [out (or/c #f output-port?)]
[in (or/c #f input-port?)]
[error-out (or/c #f output-port? 'stdout)]
[command (or/c path? string-no-nuls? bytes-no-nuls?)]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
list?]{
Like @racket[process], except that @racket[out] is used for the
process's standard output, @racket[in] is used for the process's
standard input, and @racket[error-out] is used for the process's
standard error. Any of the ports can be @racket[#f], in which case a
system pipe is created and returned, as in @racket[process]. If
@racket[error-out] is @racket['stdout], then standard error is
redirected to standard output. For each port or @racket['stdout] that
is provided, no pipe is created, and the corresponding value in the
returned list is @racket[#f].}
@defproc*[([(process*/ports [out (or/c #f output-port?)]
[in (or/c #f input-port?)]
[error-out (or/c #f output-port? 'stdout)]
[command path-string?]
[arg (or/c path? string-no-nuls? bytes-no-nuls?)]
...
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
list?]
[(process*/ports [out (or/c #f output-port?)]
[in (or/c #f input-port?)]
[error-out (or/c #f output-port? 'stdout)]
[command path-string?]
[exact 'exact]
[arg string?]
[#:set-pwd? set-pwd? any/c (member (system-type) '(unix macosx))])
list?])]{
Like @racket[process*], but with the port handling of
@racket[process/ports].}
@; ----------------------------------------------------------------------
@;section{Contract Auxiliaries}
@;note-lib[racket/system]
The contracts of @racket[system] and related functions may signal a
contract error with references to the following functions.
@defproc[(string-no-nuls? [x any/c]) boolean?]{
Ensures that @racket[x] is a string and does not contain @racket["\0"].}
@defproc[(bytes-no-nuls? [x any/c]) boolean?]{
Ensures that @racket[x] is a byte-string and does not contain @racket[#"\0"].}