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}"
Run Code Online (Sandbox Code Playgroud)
robot.rb用于Open3.popen3与sum.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
Run Code Online (Sandbox Code Playgroud)
robot.rb无法运行。似乎它停留在sum.rb's gets.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
Run Code Online (Sandbox Code Playgroud)
让我困惑的是:
robot_2.rb不像与 shell 交互,它更像是提供 shell 需要的东西,因为我只知道. 如果一个程序需要很多输入而我们无法预测顺序怎么办?
我发现如果STDOUT.flush在每个putsin之后添加sum.rb,robot.rb可以运行。但实际上我们不能相信sum.rb作者可以添加STDOUT.flush,对吗?
谢谢你的时间!
终于弄清楚如何做到这一点。使用write_nonblock和readpartial. 您必须小心的是,它的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
}
Run Code Online (Sandbox Code Playgroud)
对于正在寻找更通用的交互性(例如 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
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这不是很线程安全。这只是一个未经优化但功能齐全的解决方案,我发现它有望在未来得到改进。