Оба получают STDOUT в режиме реального времени и предоставляют STDIN - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть команда, которая требует, чтобы я дал ей некоторые данные STDIN, как в my-command <<< my-data.У меня нет контроля над командой;информация предназначена для предоставления в интерактивном режиме, и <<< работает.

Я хочу автоматизировать эту команду как часть более крупного сценария, но поскольку ее действие занимает некоторое время - но выводит прогресс в STDOUT - я хочураспечатать STDOUT в реальном времени.Я также хочу иметь возможность фиксировать состояние завершения команды, чтобы определить, не удалось ли это.

Если я использую system, я получаю STDOUT, как это происходит, но не могу предоставить STDINdata.

system('my-command')

Если я использую Open3, я могу предоставить данные STDIN, но STDOUT печатается только в конце (если я его вообще фиксирую).

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

Любойкак я могу получить лучшее из обоих миров, желательно с Open3?

1 Ответ

0 голосов
/ 11 февраля 2019

Вот фрагмент для stdout в реальном времени с необязательными данными stdin.Вам нужно использовать 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 или использовать сторонний гем.

Вы также можете записать данные stdin в временный файл и использовать собственное перенаправление оболочки:

require 'tempfile'
tempfile = Tempfile.new
tempfile.write(stdin_data)
tempfile.close
system(%(do_stuff < "#{tempfile.path}"))
tempfile.unlink
...