Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced RDoc for IO.popen #5430

Merged
merged 3 commits into from
Jan 12, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 115 additions & 49 deletions io.c
Original file line number Diff line number Diff line change
Expand Up @@ -7366,67 +7366,130 @@ static VALUE popen_finish(VALUE port, VALUE klass);

/*
* call-seq:
* IO.popen([env,] cmd, mode="r" [, opt]) -> io
* IO.popen([env,] cmd, mode="r" [, opt]) {|io| block } -> obj
* IO.popen(env = {}, cmd, mode = 'r', **opts) -> io
* IO.popen(env = {}, cmd, mode = 'r', **opts) {|io| ... } -> object
*
* Runs the specified command as a subprocess; the subprocess's
* standard input and output will be connected to the returned
* IO object.
* Executes the given command +cmd+ as a subprocess
* whose $stdin and $stdout are connected to a new stream +io+.
*
* The PID of the started process can be obtained by IO#pid method.
* If no block is given, returns the new stream,
* which depending on given +mode+ may be open for reading, writing, or both.
* The stream should be explicitly closed (eventually) to avoid resource leaks.
*
* _cmd_ is a string or an array as follows.
* If a block is given, the stream is passed to the block
* (again, open for reading, writing, or both);
* when the block exits, the stream is closed,
* and the block's value is assigned to global variable <tt>$?</tt> and returned.
*
* cmd:
* "-" : fork
* commandline : command line string which is passed to a shell
* [env, cmdname, arg1, ..., opts] : command name and zero or more arguments (no shell)
* [env, [cmdname, argv0], arg1, ..., opts] : command name, argv[0] and zero or more arguments (no shell)
* (env and opts are optional.)
* Optional argument +mode+ may be any valid \IO mode.
* See {Modes}[#class-IO-label-Modes].
*
* If _cmd_ is a +String+ ``<code>-</code>'',
* then a new instance of Ruby is started as the subprocess.
* Required argument +cmd+ determines which of the following occurs:
*
* If <i>cmd</i> is an +Array+ of +String+,
* then it will be used as the subprocess's +argv+ bypassing a shell.
jeremyevans marked this conversation as resolved.
Show resolved Hide resolved
* The array can contain a hash at first for environments and
* a hash at last for options similar to #spawn.
* - The process forks.
* - A specified program runs in a shell.
* - A specified program runs with specified arguments.
* - A specified program runs with specified arguments and a specified +argv0+.
*
* The default mode for the new file object is ``r'',
* but <i>mode</i> may be set to any of the modes listed in the description for class IO.
* The last argument <i>opt</i> qualifies <i>mode</i>.
* Each of these is detailed below.
*
* # set IO encoding
* IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io|
* euc_jp_string = nkf_io.read
* }
* The optional hash argument +env+ specifies name/value pairs that are to be added
* to the environment variables for the subprocess:
*
* # merge standard output and standard error using
* # spawn option. See the document of Kernel.spawn.
* IO.popen(["ls", "/", :err=>[:child, :out]]) {|ls_io|
* ls_result_with_error = ls_io.read
* }
* IO.popen({'FOO' => 'bar'}, 'ruby', 'r+') do |pipe|
* pipe.puts 'puts ENV["FOO"]'
* pipe.close_write
* pipe.gets
* end => "bar\n"
*
* # spawn options can be mixed with IO options
* IO.popen(["ls", "/"], :err=>[:child, :out]) {|ls_io|
* ls_result_with_error = ls_io.read
* }
* The optional keyword arguments +opts+ may be {\IO open options}[#class-IO-label-Open+Options]
* and options for Kernel#spawn.
*
* <b>Forked \Process</b>
*
* When argument +cmd+ is the 1-character string <tt>'-'</tt>, causes the process to fork:
* IO.popen('-') do |pipe|
* if pipe
* $stderr.puts "In parent, child pid is #{pipe.pid}\n"
* else
* $stderr.puts "In child, pid is #{$$}\n"
* end
* end
*
* Output:
*
* In parent, child pid is 26253
* In child, pid is 26253
*
* Raises exceptions which IO.pipe and Kernel.spawn raise.
* Note that this is not supported on all platforms.
*
* <b>Shell Subprocess</b>
*
* When argument +cmd+ is a single string (but not <tt>'-'</tt>),
* the program named +cmd+ is run as a shell command:
*
* IO.popen('uname') do |pipe|
* pipe.readlines
* end
*
* Output:
*
* ["Linux\n"]
*
* Another example:
*
* IO.popen('/bin/sh', 'r+') do |pipe|
* pipe.puts('ls')
* pipe.close_write
* $stderr.puts pipe.readlines.size
* end
*
* Output:
*
* If a block is given, Ruby will run the command as a child connected
* to Ruby with a pipe. Ruby's end of the pipe will be passed as a
* parameter to the block.
* At the end of block, Ruby closes the pipe and sets <code>$?</code>.
jeremyevans marked this conversation as resolved.
Show resolved Hide resolved
* In this case IO.popen returns the value of the block.
* 213
*
* If a block is given with a _cmd_ of ``<code>-</code>'',
* the block will be run in two separate processes: once in the parent,
* and once in a child. The parent process will be passed the pipe
* object as a parameter to the block, the child version of the block
* will be passed +nil+, and the child's standard in and
* standard out will be connected to the parent through the pipe. Not
* available on all platforms.
* <b>Program Subprocess</b>
*
* When argument +cmd+ is an array of strings,
* the program named <tt>cmd[0]</tt> is run with all elements of +cmd+ as its arguments:
*
* IO.popen(['du', '..', '.']) do |pipe|
* $stderr.puts pipe.readlines.size
* end
*
* Output:
*
* 1111
*
* <b>Program Subprocess with <tt>argv0</tt></b>
*
* When argument +cmd+ is an array whose first element is a 2-element string array
* and whose remaining elements (if any) are strings:
*
* - <tt>cmd[0][0]</tt> (the first string in the nested array) is the name of a program that is run.
* - <tt>cmd[0][1]</tt> (the second string in the nested array) is set as the program's <tt>argv[0]</tt>.
* - <tt>cmd[1..-1] (the strings in the outer array) are the program's arguments.
*
* Example (sets <tt>$0</tt> to 'foo'):
*
* IO.popen([['/bin/sh', 'foo'], '-c', 'echo $0']).read # => "foo\n"
*
* <b>Some Special Examples</b>
*
* # Set IO encoding.
* IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io|
* euc_jp_string = nkf_io.read
* }
*
* # Merge standard output and standard error using Kernel#spawn option. See Kernel#spawn.
* IO.popen(["ls", "/", :err=>[:child, :out]]) do |io|
* ls_result_with_error = io.read
* end
*
* # Use mixture of spawn options and IO options.
* IO.popen(["ls", "/"], :err=>[:child, :out]) do |io|
* ls_result_with_error = io.read
* end
*
* f = IO.popen("uname")
* p f.readlines
Expand All @@ -7439,7 +7502,7 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* f.puts "bar"; f.close_write; puts f.gets
* }
*
* <em>produces:</em>
* Output (from last section):
*
* ["Linux\n"]
* Parent is 21346
Expand All @@ -7448,6 +7511,9 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* 21352 is here, f is nil
* #<Process::Status: pid 21352 exit 0>
* <foo>bar;zot;
*
* Raises exceptions that IO.pipe and Kernel.spawn raise.
*
*/

static VALUE
Expand Down