0

我正在编写一个 Thor 脚本来从不同的工具运行一些测试,即运行一个 shell 命令。我希望命令中的 stdout 和 stderr 不断流入我的控制台。

第一次尝试是只使用反引号,但自然不会打印标准输出/标准错误(相反,标准输出被捕获在返回值中)。

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

它看起来是不是最好和最干净的方法?我必须在标准错误之前手动尝试打印标准输出是一个问题吗?

4

2 回答 2

0

在我看来,运行 shell 命令而不是尝试捕获 stdout 或 stderr 的最简单方法(相反,让它们在它们出现时冒泡)是这样的:

def run *args, **options
  pid = spawn(*args, options)
  pid, status = Process.wait2(pid)
  exit(status.exitstatus) unless status.success?
end

反引号的问题 orsystem()是前者捕获标准输出,而后者只返回命令是否成功。spawn()是比system(). 我宁愿让我的 Thor 脚本工具失败,就好像它只是那些 shell 命令的包装器一样。

于 2017-10-02T13:39:58.823 回答
0

我正在使用IO.popen类似的任务,如下所示: IO.popen([env, *command]) do |io| io.each { |line| puts ">>> #{line}" } end 要捕获 stderr 我只需将其重定向到 stdoutcommand = %w(run-my-tests.sh 2>&1)

更新 我已经构建了一个脚本,Open3::popen3用于分别捕获标准输出和标准错误。它显然有很多房间形式的改进,但基本概念希望是明确的。

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
于 2017-09-25T15:49:36.883 回答