无论是在实时获得STDOUT和STDIN提供

问题描述 投票:0回答:1

我有需要我给它一些标准输入数据,在my-command <<< my-data的命令。我没有在命令控制;该信息是指以交互方式给出<<<工作。

我想这个命令作为一个更大的脚本的一部分自动化,但由于其作用需要一段时间 - 但输出进步STDOUT - 我想打印STDOUT的实时性。我也希望能够捕捉到该命令的退出状态,以确定它是否失败与否。

如果我使用system,我得到的标准输出,因为它发生,但不能提供标准输入数据。

system('my-command')

如果我使用Open3,我可以提供STDIN数据,但STDOUT仅在端印刷(如果我捕捉它的话)。

Open3.capture2('my-command', stdin_data: 'my-data')[1].success?

任何办法可以两全其美的,最好用Open3

ruby stdout stdin popen3
1个回答
0
投票

这是一个有可选的标准输入数据的实时标准输出的一个片段。您需要使用IO.select窥视流,看看他们是否可读。

require 'open3'

class Runner
  class Result
    attr_accessor :status
    attr_reader :stdout, :stderr

    def initialize
      @stdout = +''
      @stderr = +''
      @status = -127
    end

    def success?
      status.zero?
    end
  end

  attr_reader :result

  def initialize(cmd, stdin: nil, print_to: nil)
    @stdin = stdin
    @cmd = cmd
    @result = Result.new
    @print_to = print_to
  end

  def run
    Open3.popen3(@cmd) do |stdin, stdout, stderr, wait_thr|
      # Dump the stdin-data into the command's stdin:
      unless stdin.closed?
        stdin.write(@stdin) if @stdin
        stdin.close
      end

      until [stdout, stderr].all?(&:eof?)
        readable = IO.select([stdout, stderr])
        next unless readable&.first

        readable.first.each do |stream|
          data = +''
          begin
            stream.read_nonblock(1024, data)
          rescue EOFError
            # ignore, it's expected for read_nonblock to raise EOFError
            # when all is read
          end

          next if data.empty?

          if stream == stdout
            result.stdout << data
            @print_to << data if @print_to
          else
            result.stderr << data
            @print_to << data if @print_to
          end
        end
      end

      result.status = wait_thr.value.exitstatus
    end
    result
  end
end


result = Runner.new('ls -al').run
puts "Exit status: %d" % result.status
puts "Stdout:"
puts result.stdout
puts "Stderr:"
puts result.stderr

# Print as it goes:
result = Runner.new('ls -al', print_to: $stdout).run

如果您需要模拟实时标准输入(按键),那么你就需要建立某种形式的匹配对于stdout数据流的,当预期的提示来通过流写入命令的标准输入的响应。在这种情况下,你可能会更好过与PTY.spawn或者使用第三方的宝石。

你也可以在标准输入数据写入一个临时文件和使用shell自己的重定向:

require 'tempfile'
tempfile = Tempfile.new
tempfile.write(stdin_data)
tempfile.close
system(%(do_stuff < "#{tempfile.path}"))
tempfile.unlink
© www.soinside.com 2019 - 2024. All rights reserved.