ruby popen3 - как многократно писать на стандартный ввод и читать стандартный вывод без повторного открытия?

Я использую метод Openen3 для Open3, чтобы запустить процесс, работающий в режиме консоли / REPL, чтобы многократно принимать ввод и возвращать вывод.

Я могу открыть процесс, отправить ввод и получить вывод просто отлично, с кодом, подобным этому:

Open3.popen3("console_REPL_process") do |stdin, stdout, stderr, wait_thr|
    stdin.puts "a string of input"
    stdin.close_write
    stdout.each_line { |line| puts line } #successfully prints all the output
end

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

Я знаю, что для закрытия stdout нужно закрыть stdin... но я не знаю, как мне открыть stdin, чтобы я мог написать больше входных данных?

В идеале я хочу сделать что-то вроде этого:

Open3.popen3("console_REPL_process") do |stdin, stdout, stderr, wait_thr|
    stdin.puts "a string of input"
    stdin.close_write
    stdout.each_line { |line| puts line }

    stdin.reopen_somehow()

    stdin.puts "another string of input"
    stdin.close_write
    stdout.each_line { |line| puts line }
    # etc..
end

решение

Благодаря ответу pmoo, я смог найти решение, используя PTY а также expect, ожидая строку приглашения, которую процесс возвращает всякий раз, когда он готов для дополнительного ввода, например так:

PTY.spawn("console_REPL_process") do |output, input|
    output.expect("prompt >") do |result|
      input.puts "string of input"
    end
    output.expect("prompt >") do |result|
      puts result
      input.puts "another string of input"
    end
    output.expect("prompt >") do |result|
      puts result
      input.puts "a third string of input"
    end
    # and so forth
end

1 ответ

Решение

Вы можете иметь некоторый успех, используя expect библиотека, и иметь дочерний процесс, чтобы явно отметить конец каждого вывода, например:

require 'expect'
require 'open3'

Open3.popen3("/bin/bash") do
    | input, output, error, wait_thr |
    input.sync = true
    output.sync = true

    input.puts "ls /tmp"
    input.puts "echo '----'"
    puts output.expect("----", 5)

    input.puts "cal apr 2014"
    input.puts "echo '----'"
    puts output.expect("----", 5)
end

В качестве бонуса, expect имеет timeout вариант.

Другие вопросы по тегам