我正在编写一个Thor脚本,以便从另一个工具运行一些测试,即运行shell命令。我喜欢命令中的stdout和stderr不断流入我的控制台。
首次尝试只是使用反引号,但自然不会打印stdout / stderr(而是在返回值中捕获stdout)。
desc "mytask", "my description"
def mytask
`run-my-tests.sh`
end
我的下一个方法是使用Open3,如下所示:
require "open3"
desc "mytask", "my description"
def mytask
Open3.popen3("run-my-tests.sh") do |stdin, stdout, stderr|
STDOUT.puts(stdout.read())
STDERR.puts(stderr.read())
end
end
但是,上面的方法将从stdout和stderr获得整个输出,并且只在最后打印。在我的用例中,我更倾向于看到失败并通过测试的输出。
从http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html开始,我看到我们可以通过块来读取流,即gets()
而不是read()
。例如:
require "open3"
desc "mytask", "my description"
def mytask
Open3.popen3(command) do |stdin, stdout, stderr|
while (out = stdout.gets()) || err = (stderr.gets())
STDOUT.print(out) if out
STDERR.print(err) if err
end
exit_code = wait_thr.value
unless exit_code.success?
raise "Failure"
end
end
end
它看起来是最好,最干净的方法吗?是否需要在 stderr之前手动尝试打印stdout ?
答案 0 :(得分:0)
我正在使用IO.popen
执行类似任务,如下所示:
IO.popen([env, *command]) do |io|
io.each { |line| puts ">>> #{line}" }
end
要捕获stderr,我只需将其重定向到stdout command = %w(run-my-tests.sh 2>&1)
<强>更新强>
我使用Open3::popen3
构建了一个脚本来分别捕获stdout和stderr。它显然有很多房型改进,但基本的想法很有希望。
require 'open3'
command = 'for i in {1..5}; do echo $i; echo "$i"err >&2; sleep 0.5; done'
stdin, stdout, stderr, _command_thread = Open3.popen3(command)
reading_thread = Thread.new do
kilobyte = 1024
loop do
begin
stdout.read_nonblock(kilobyte).lines { |line| puts "stdout >>> #{line}" }
stderr.read_nonblock(kilobyte).lines { |line| puts "stderr >>> #{line}" }
rescue IO::EAGAINWaitReadable
next
rescue EOFError
break
end
sleep 1
end
end
reading_thread.join
stdin.close
stdout.close
stderr.close
答案 1 :(得分:0)
对我而言,似乎是运行shell命令的最简单方法,而不尝试捕获stdout或stderr(相反,让它们在它们出现时冒泡)是这样的:
def run *args, **options
pid = spawn(*args, options)
pid, status = Process.wait2(pid)
exit(status.exitstatus) unless status.success?
end
反引号或system()
的问题是前者捕获stdout而后者只返回命令是否成功。 spawn()
是system()
的更具信息性的替代方案。我宁愿让我的Thor脚本工具失败,好像它只是那些shell命令的包装器。