ruby popen3 — how to repeatedly write to stdin & read stdout without re-opening process?

时光怂恿深爱的人放手 提交于 2019-12-01 14:43:56

问题


I am using Open3's popen3 method to start a process that functions in a console-like / REPL fashion to repeatedly accept input and return output.

I am able to open the process, send input, and receive the output just fine, with code like this:

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

I want to do that many times in a row, without re-opening the process, as it takes a long time to start up.

I know I have to close stdin in order for stdout to return.. but what I don't know is, how do I 'reopen' stdin so I can write more input?

Ideally I want to do something like this:

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

solution

Thanks to pmoo's answer, I was able to devise a solution using PTY and expect, expecting the prompt string that the process returns whenever it is ready for more input, like so:

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:


You can have some success using expect library, and have the child process to explicitly mark the end of each output, like:

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

As a bonus, expect has a timeout option.



来源:https://stackoverflow.com/questions/22851698/ruby-popen3-how-to-repeatedly-write-to-stdin-read-stdout-without-re-opening

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!