我有一条命令要求我给它一些STDIN数据,如my-command <<< my-data
中所示。我无法控制该命令;该信息应以交互方式提供,<<<
有效。
我希望将此命令作为较大脚本的一部分进行自动化,但是由于它的操作需要一些时间-但是将进度输出到STDOUT-我想实时打印STDOUT。我还希望能够捕获命令的退出状态,以确定它是否失败。
如果我使用system
,则会得到STDOUT,但不能提供STDIN数据。
system('my-command')
如果我使用Open3
,我可以提供STDIN数据,但STDOUT仅打印在末尾(如果我完全捕获的话)。
Open3.capture2('my-command', stdin_data: 'my-data')[1].success?
以任何方式我都可以做到两全其美,最好是使用Open3
?
答案 0 :(得分:0)
这是带有可选标准输入数据的实时标准输出的代码段。您需要使用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
如果需要模拟实时stdin(按键),则需要为stdout数据流创建某种匹配器,并在预期的提示通过流时将响应写入命令的stdin。在这种情况下,最好使用PTY.spawn
或使用第三方宝石。
您还可以将stdin数据写入临时文件并使用Shell自身的重定向:
require 'tempfile'
tempfile = Tempfile.new
tempfile.write(stdin_data)
tempfile.close
system(%(do_stuff < "#{tempfile.path}"))
tempfile.unlink