@@ -258,32 +258,69 @@ def self.run(*command, **options_hash)
258258 #
259259 # Accepts all [Process.spawn execution
260260 # options](https://docs.ruby-lang.org/en/3.4/Process.html#module-Process-label-Execution+Options),
261- # the additional options defined by {spawn_with_timeout} and {run}, and the additional
262- # option `merge_output`:
263- #
264- # * `merge_output: true` merges stdout and stderr into a single capture buffer
265- # (default is false)
261+ # the additional options defined by {spawn_with_timeout} and {run}, and the
262+ # additional options `merge_output`, `encoding`, `stdout_encoding`, and
263+ # `stderr_encoding`:
264+ #
265+ # * `merge_output: <Boolean>` if true merges stdout and stderr into a single
266+ # capture buffer (default is false)
267+ # * `encoding: <Encoding>` sets the encoding for both stdout and stderr captures
268+ # (default is `Encoding::UTF_8`)
269+ # * `stdout_encoding: <Encoding>` sets the encoding for the stdout capture and, if
270+ # not nil, overrides the `encoding` option for stdout (default is nil)
271+ # * `stderr_encoding: <Encoding>` sets the encoding for the stderr capture and, if
272+ # not nil, overrides the `encoding` option for stderr (default is nil)
266273 #
267274 # The captured output is accessed in the returned object's `#stdout` and `#stderr`
268275 # methods. Merged output (if the `merged_output: true` option is given) is accessed
269276 # in the `#stdout` method.
270277 #
271- # stdout and stderr redirection options may be given by the user. User-supplied
272- # redirections will receive the output in addition to the internal capture.
278+ # stdout and stderr redirection destinations may be given by the user (e.g. `out:
279+ # <destination>` or `err: <destination>`). These redirections will receive the
280+ # output in addition to the internal capture.
281+ #
282+ # Unless told otherwise, the internally captured output is assumed to be in UTF-8
283+ # encoding. This assumption can be changed with the `encoding`,
284+ # `stdout_encoding`, or `stderr_encoding` options. These options accept any
285+ # encoding objects returned by `Encoding.list` or their String equivalent given by
286+ # `#to_s`.
287+ #
288+ # The bytes captured are not transcoded. They are interpreted as being in the
289+ # specified encoding. The user will have to check the validity of the
290+ # encoding by calling `#valid_encoding?` on the captured output (e.g.,
291+ # `result.stdout.valid_encoding?`).
273292 #
274293 # A `ProcessExecuter::ArgumentError` will be raised if both an options object and
275294 # an options_hash are given.
276295 #
277296 # @example capture stdout and stderr
278- # result = ProcessExecuter.run_with_capture('echo HELLO; echo ERROR >&2')
279- # result.stdout #=> " HELLO\n"
280- # result.stderr #=> "ERROR\n"
297+ # result =
298+ # ProcessExecuter.run_with_capture('echo HELLO; echo ERROR >&2')
299+ # result.stdout #=> "HELLO\n" result. stderr #=> "ERROR\n"
281300 #
282301 # @example merge stdout and stderr
283302 # result = ProcessExecuter.run_with_capture('echo HELLO; echo ERROR >&2', merge_output: true)
284303 # # order of output is not guaranteed
285- # result.stdout #=> "HELLO\nERROR\n"
286- # result.stderr #=> ""
304+ # result.stdout #=> "HELLO\nERROR\n" result.stderr #=> ""
305+ #
306+ # @example default encoding
307+ # result = ProcessExecuter.run_with_capture('echo HELLO')
308+ # result.stdout #=> "HELLO\n"
309+ # result.stdout.encoding #=> #<Encoding:UTF-8>
310+ # result.stdout.valid_encoding? #=> true
311+ #
312+ # @example custom encoding
313+ # result = ProcessExecuter.run_with_capture('echo HELLO', encoding: Encoding::ISO_8859_1)
314+ # result.stdout #=> "HELLO\n"
315+ # result.stdout.encoding #=> #<Encoding:ISO-8859-1>
316+ # result.stdout.valid_encoding? #=> true
317+ #
318+ # @example custom encoding with invalid bytes
319+ # File.binwrite('output.txt', "\xFF\xFE") # little-endian BOM marker is not valid UTF-8
320+ # result = ProcessExecuter.run_with_capture('cat output.txt')
321+ # result.stdout #=> "\xFF\xFE"
322+ # result.stdout.encoding #=> #<Encoding:UTF-8>
323+ # result.stdout.valid_encoding? #=> false
287324 #
288325 # @overload run_with_capture(*command, **options_hash)
289326 #
@@ -301,6 +338,24 @@ def self.run(*command, **options_hash)
301338 # @option options_hash [Boolean] :merge_output if true, stdout and stderr will be
302339 # merged into a single capture buffer
303340 #
341+ # @option options_hash [Encoding, String] :encoding the encoding to assume for
342+ # the internal stdout and stderr captures
343+ #
344+ # The default is `Encoding::UTF_8`. This option is overridden by the `stdout_encoding`
345+ # and `stderr_encoding` options if they are given and not nil.
346+ #
347+ # @option options_hash [Encoding, String, nil] :stdout_encoding the encoding to
348+ # assume for the internal stdout capture
349+ #
350+ # The default is nil, which means the `encoding` option is used. If this option is
351+ # is not nil, it is used instead of the `encoding` option.
352+ #
353+ # @option options_hash [Encoding, String, nil] :stderr_encoding the encoding to
354+ # assume for the internal stderr capture
355+ #
356+ # The default is nil, which means the `encoding` option is used. If this option
357+ # is not nil, it is used instead of the `encoding` option.
358+ #
304359 # @overload run_with_capture(*command, options)
305360 #
306361 # @param command [Array<String>] see [Process module, Argument `command_line` or
@@ -337,6 +392,9 @@ def self.run(*command, **options_hash)
337392 #
338393 # @return [ProcessExecuter::ResultWithCapture]
339394 #
395+ # Where `#stdout` and `#stderr` are strings whose encoding is determined by the
396+ # `:encoding`, `:stdout_encoding`, or `:stderr_encoding` options.
397+ #
340398 # @api public
341399 #
342400 def self . run_with_capture ( *command , **options_hash )
0 commit comments