Skip to content

Commit b658b0b

Browse files
Improve documentation for Process.run & co (#16618)
1 parent ec7f89e commit b658b0b

File tree

1 file changed

+95
-20
lines changed

1 file changed

+95
-20
lines changed

src/process.cr

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -177,26 +177,52 @@ class Process
177177
alias ExecStdio = Redirect | IO::FileDescriptor
178178
alias Env = Nil | Hash(String, Nil) | Hash(String, String?) | Hash(String, String)
179179

180-
# Executes a process and waits for it to complete.
180+
# Executes a child process and waits for it to complete, returning its status.
181181
#
182-
# By default the process is configured without input, output or error.
182+
# See `Process.new` for the meaning of the parameters.
183183
#
184-
# Raises `IO::Error` if executing the command fails (for example if the executable doesn't exist).
184+
# Returns a `Process::Status` representing the child process' exit status.
185+
# The global `$?` variable is set to the returned status.
186+
#
187+
# Raises `IO::Error` if the execution itself fails (for example because the
188+
# executable does not exist or is not executable).
189+
#
190+
# Example:
191+
#
192+
# ```
193+
# status = Process.run("echo", ["hello"], output: Process::Redirect::Inherit)
194+
# # outputs "hello\n"
195+
# $? # => Process::Status[0]
196+
# status # => Process::Status[0]
197+
# ```
185198
def self.run(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,
186199
input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil) : Process::Status
187200
status = new(command, args, env, clear_env, shell, input, output, error, chdir).wait
188201
$? = status
189202
status
190203
end
191204

192-
# Executes a process, yields the block, and then waits for it to finish.
205+
# Executes a child process, yields the block, and then waits for it to finish.
193206
#
194-
# By default the process is configured to use pipes for input, output and error. These
195-
# will be closed automatically at the end of the block.
207+
# See `Process.new` for the meaning of the parameters.
208+
#
209+
# By default the process is configured to use pipes for input, output and error.
210+
# These will be closed automatically at the end of the block.
196211
#
197212
# Returns the block's value.
198213
#
199-
# Raises `IO::Error` if executing the command fails (for example if the executable doesn't exist).
214+
# Raises `IO::Error` if the execution itself fails (for example because the
215+
# executable does not exist or is not executable).
216+
#
217+
# Example:
218+
#
219+
# ```
220+
# output = Process.run("echo", ["hello"]) do |process|
221+
# process.output.gets_to_end
222+
# end
223+
# $? # => Process::Status[0]
224+
# output # => "hello\n"
225+
# ```
200226
def self.run(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,
201227
input : Stdio = Redirect::Pipe, output : Stdio = Redirect::Pipe, error : Stdio = Redirect::Pipe, chdir : Path | String? = nil, &)
202228
process = new(command, args, env, clear_env, shell, input, output, error, chdir)
@@ -260,24 +286,73 @@ class Process
260286
@process_info : Crystal::System::Process
261287
@wait_count = 0
262288

263-
# Creates a process, executes it, but doesn't wait for it to complete.
289+
# Creates and executes a child process.
264290
#
265-
# To wait for it to finish, invoke `wait`.
291+
# This starts a new process for `command`.
266292
#
267-
# By default the process is configured without input, output or error.
293+
# ## `shell: false` (the default)
268294
#
269-
# If *shell* is false, the *command* is the path to the executable to run,
270-
# along with a list of *args*.
295+
# *command* is either a path to the executable to run, or the name of an
296+
# executable which is then looked up by the operating system.
297+
# The lookup uses the `PATH` variable of the current process environment
298+
# (i.e. `ENV["PATH"]).
299+
# In order to resolve to a specific executable, provide a path instead of
300+
# only a command name. `Process.find_executable` can help with looking up a
301+
# command in a custom `PATH`.
271302
#
272-
# If *shell* is true, the *command* should be the full command line
273-
# including space-separated args.
274-
# * On POSIX this uses `/bin/sh` to process the command string. *args* are
275-
# also passed to the shell, and you need to include the string `"${@}"` in
276-
# the *command* to safely insert them there.
277-
# * On Windows this is implemented by passing the string as-is to the
278-
# process, and passing *args* is not supported.
303+
# The arguments in *args* are passed as arguments to the child process.
279304
#
280-
# Raises `IO::Error` if executing the command fails (for example if the executable doesn't exist).
305+
# Raises `IO::Error` if executing *command* fails, for example because the
306+
# executable doesn't exist or is not executable.
307+
#
308+
# ## `shell: true`
309+
#
310+
# *command* is a shell script executed in the system shell (`/bin/sh` on Unix
311+
# systems, `cmd.exe` on Windows).
312+
# Command names are looked up by the shell itself, using the `PATH` variable
313+
# of the shell process (i.e. `env["PATH"]`).
314+
#
315+
# *args* is unsupported on Windows.
316+
# On Unix it's passed as additional arguments to the shell process and can be
317+
# used in the shell script with `"${@}"` to safely insert them there. If the
318+
# script is a single command (no whitespace), `"${@}"` is appended implicitly.
319+
#
320+
# The returned instance represents the shell process, not the process executed
321+
# for *command*.
322+
#
323+
# If executing *command* fails, for example because the executable doesn't
324+
# exist or is not executable, it may raise `IO::Error` (on Windows) or return
325+
# an unsuccessful exit status (on Unix).
326+
#
327+
# ## Shared parameters
328+
#
329+
# *env* provides a mapping of environment variables for the child process.
330+
# If *clear_env* is `true`, only these explicit variables are used; if `false`,
331+
# the child inherits the parent's environment with *env* merged.
332+
#
333+
# *input*, *output*, *error* configure the child process's standard streams.
334+
# * `Redirect::Close` passes the null device
335+
# * `Redirect::Pipe` creates a pipe that's accessible via `#input`, `#output`
336+
# or `#error`.
337+
# * `Redirect::Inherit` to share the parent's streams (`STDIN`, `STDOUT`, `STDERR`).
338+
# * An `IO` instance creates a pipe that reads/writes into the given IO.
339+
#
340+
# *chdir* changes the working directory of the child process. If `nil`, uses
341+
# the current working directory of the parent process.
342+
#
343+
# Example:
344+
#
345+
# ```
346+
# process = Process.new("echo", ["Hello"], output: Process::Redirect::Pipe)
347+
# process.output.gets_to_end # => "Hello\n"
348+
# process.wait # => Process::Status[0]
349+
# ```
350+
#
351+
# Similar methods:
352+
#
353+
# * `Process.run` is a convenient short cut if you just want to run a command
354+
# and wait for it to finish.
355+
# * `Process.exec` replaces the current process.
281356
def initialize(command : String, args : Enumerable(String)? = nil, env : Env = nil, clear_env : Bool = false, shell : Bool = false,
282357
input : Stdio = Redirect::Close, output : Stdio = Redirect::Close, error : Stdio = Redirect::Close, chdir : Path | String? = nil)
283358
fork_input = stdio_to_fd(input, for: STDIN)

0 commit comments

Comments
 (0)