
Also use `byte?' instead of (integer-in 0 255). (This commit will most likely require some proof-reading.)
511 lines
20 KiB
Racket
511 lines
20 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. See also @racket[system] and
|
|
@racket[process] from @racketmodname[racket/system].
|
|
|
|
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 X, 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 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].
|
|
|
|
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.}
|
|
|
|
|
|
@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 X:
|
|
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 X:
|
|
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 X: 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?)]) boolean?]{
|
|
|
|
Executes a Unix, Mac OS X, 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.
|
|
|
|
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?)] ...)
|
|
boolean?]
|
|
[(system* [command path-string?] [exact 'exact] [arg string?])
|
|
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?)])
|
|
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?)] ...)
|
|
byte?]
|
|
[(system*/exit-code [command path-string?]
|
|
[exact 'exact] [arg string?])
|
|
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?)])
|
|
(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 X, @exec{cmd} on Windows). The result is a list of five
|
|
values:
|
|
|
|
@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 X, 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].
|
|
|
|
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?)] ...)
|
|
list?]
|
|
[(process* [command path-string?] [exact 'exact] [arg string?])
|
|
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?)])
|
|
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?)]
|
|
...)
|
|
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?])
|
|
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"].}
|