Method: Subprocess::Process#communicate

Defined in:
lib/subprocess.rb

#communicate(input = nil) ⇒ Array<String>

Write the (optional) input to the process’s stdin. Also, read (and buffer in memory) the contents of stdout and stderr. Do this all using IO::select, so we don’t deadlock due to full pipe buffers.

This is only really useful if you set some of :stdin, :stdout, and :stderr to Subprocess::PIPE.

Parameters:

  • input (String) (defaults to: nil)

    A string to feed to the child’s standard input.

Returns:

  • (Array<String>)

    An array of two elements: the data read from the child’s standard output and standard error, respectively.

Raises:

  • (ArgumentError)


428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/subprocess.rb', line 428

def communicate(input=nil)
  raise ArgumentError if !input.nil? && @stdin.nil?

  stdout, stderr = "", ""
  input = input.dup unless input.nil?

  @stdin.close if (input.nil? || input.empty?) && !@stdin.nil?

  self_read, self_write = IO.pipe
  self.class.catching_sigchld(pid, self_write) do
    wait_r = [@stdout, @stderr, self_read].compact
    wait_w = [input && @stdin].compact
    loop do
      ready_r, ready_w = select(wait_r, wait_w)

      # If the child exits, we still have to be sure to read any data left
      # in the pipes. So we poll the child, drain all the pipes, and *then*
      # check @status.
      #
      # It's very important that we do not call poll between draining the
      # pipes and checking @status. If we did, we open a race condition
      # where the child writes to stdout and exits in that brief window,
      # causing us to lose that data.
      poll

      if ready_r.include?(@stdout)
        if drain_fd(@stdout, stdout)
          wait_r.delete(@stdout)
        end
      end

      if ready_r.include?(@stderr)
        if drain_fd(@stderr, stderr)
          wait_r.delete(@stderr)
        end
      end

      if ready_r.include?(self_read)
        if drain_fd(self_read)
          raise "Unexpected internal error -- someone closed our self-pipe!"
        end
      end

      if ready_w.include?(@stdin)
        begin
          written = @stdin.write_nonblock(input)
        rescue EOFError # Maybe I shouldn't catch this...
        rescue Errno::EINTR
        end
        input[0...written] = ''
        if input.empty?
          @stdin.close
          wait_w.delete(@stdin)
        end
      end

      break if @status

      # If there's nothing left to wait for, we're done!
      break if wait_r.length == 0 && wait_w.length == 0
    end
  end

  wait

  [stdout, stderr]
end