【问题标题】:ruby open3 stdout and stdin how to interactruby open3 stdout和stdin如何交互
【发布时间】:2017-08-18 23:31:13
【问题描述】:

sum.rb 非常简单。您输入两个数字,它会返回总和。

# sum.rb
puts "Enter number A"
a = gets.chomp
puts "Enter number B"
b = gets.chomp
puts "sum is #{a.to_i + b.to_i}"

robot.rb 使用Open3.popen3sum.rb 进行交互。代码如下:

# robot.rb
require 'open3'

Open3.popen3('ruby sum.rb') do |stdin, stdout, stderr, wait_thr| 
  while line = stdout.gets
    if line == "Enter number A\n"
      stdin.write("10\n")
    elsif line == "Enter number B\n"
      stdin.write("30\n")
    else
      puts line
    end
  end
end

robot.rb 运行失败。似乎卡在sum.rbgets.chomp

后来我发现我必须写如下才能使它工作。您需要事先以正确的顺序输入输入。

# robot_2.rb
require 'open3'

Open3.popen3('ruby sum.rb') do |stdin, stdout, stderr, wait_thr| 
  stdin.write("10\n")
  stdin.write("30\n")
  puts stdout.read
end

让我感到困惑的是:

  1. robot_2.rb 不像 与 shell 交互,它更像是 满足 shell 需要的东西,因为我只知道。如果一个程序需要很多输入而我们无法预测顺序怎么办?

  2. 我发现STDOUT.flush 是否在sum.rb 中的每个puts 之后添加,robot.rb 可以运行。但实际上我们不能相信sum.rb的作者可以加STDOUT.flush,对吧?

感谢您的宝贵时间!

【问题讨论】:

    标签: ruby popen3


    【解决方案1】:

    终于想出了如何做到这一点。使用write_nonblockreadpartial。您需要注意的是 stdout.readpartial 完全按照它所说的那样做,这意味着您将不得不汇总数据并通过查找换行符自己执行 gets

    require 'open3'
    env = {"FOO"=>"BAR", "BAZ"=>nil}
    options = {}
    Open3.popen3(env, "cat", **options) {|stdin, stdout, stderr, wait_thr|
        stdin.write_nonblock("hello")
    
        puts stdout.readpartial(4096)
        # the magic 4096 is just a size of memory from this example:
        # https://apidock.com/ruby/IO/readpartial
    
    
        stdin.close
        stdout.close
        stderr.close
        wait_thr.join
    }
    

    对于寻求更通用交互性(例如 ssh 交互)的人,您可能希望创建单独的线程来聚合标准输出和触发标准输入。

    require 'open3'
    env = {"FOO"=>"BAR", "BAZ"=>nil}
    options = {}
    unprocessed_output = ""
    Open3.popen3(env, "cat", **options) {|stdin, stdout, stderr, wait_thr|
    
        on_newline = ->(new_line) do
            puts "process said: #{new_line}"
            # close after a particular line
            stdin.close
            stdout.close
            stderr.close
        end
    
        Thread.new do
            while not stdout.closed? # FYI this check is probably close to useless/bad
                unprocessed_output += stdout.readpartial(4096)
                if unprocessed_output =~ /(.+)\n/
                    # extract the line
                    new_line = $1
                    # remove the line from unprocessed_output
                    unprocessed_output.sub!(/(.+)\n/,"")
                    # run the on_newline
                    on_newline[new_line]
                end
    
                # in theres no newline, this process will hang forever
                # (e.g. probably want to add a timeout)
            end
        end
    
        stdin.write_nonblock("hello\n")
    
        wait_thr.join
    }
    

    顺便说一句,这不是线程安全的。这只是我发现的一个未优化但功能强大的解决方案,希望在未来得到改进。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-14
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 1970-01-01
      • 2017-04-06
      • 2015-10-22
      • 1970-01-01
      相关资源
      最近更新 更多