如何包含使用 FileUtils#sh 执行命令的管道?

How to include a pipe executing a command with FileUtils#sh?

我知道 Ruby 足够危险,所以我主要通过 API 文档中的示例编写一堆构建自动化脚本作为 Rake 任务。

我正在编写一堆构建命令,代码如下:

results = {}
schemes.each { |scheme|
    command = "xcodebuild", "-scheme", scheme
    if options.configuration
        command.push "-configuration", options.configuration
    end
    command.push "archive"

    sh *command do |ok, res|
        results[scheme] = ok
    end
}

这工作正常,但是将 lot 的输出转储到控制台,这有点碍事。所以我很想通过 xcpretty 将这些东西通过管道重新格式化。

如果我实际上 运行 来自我的终端的命令,它工作正常:

xcodebuild -scheme Foo -configuration Release archive | xcpretty

但是如果我修改 Rake 任务以将这些部分附加到命令数组的末尾,sh 认为它们是 xcodebuild 的参数。

    command.push "archive", "|", "xcpretty"

我见过很多使用 sh 管道的字符串形式的例子,而不是数组形式。由于我正在构建的命令的性质——可选参数、带空格和其他字符的参数——数组形式处理起来更简洁。但是有没有办法在使用时包含输出管道?

一个好的解决方案是使用 Open3,Ruby 的标准部分,它有很多用于打开进程和安排它们的管道的实用程序。

这是一个对我有用的例子:

require 'open3'
Open3.pipeline(['ls', '-l'], ['grep', 'z'])

当您 运行 执行此操作时,管道中最后一个命令的标准输出管道将连接到 Ruby 进程的预先存在的标准输出管道。这意味着您将能够在生成任何输出时立即看到它,而不是等待进程结束。这种方法的另一个好处是它不会产生 shell;它只是直接生成进程。

另一种选择是简单地使用 Array#join 将参数与空格连接在一起,然后再将它们传递给 sh。您只需要注意您的论点中可能有空格的可能性,如果是这样,那么您必须正确地引用或转义它们。此方法会产生一个 shell 来处理您的命令,它还应该为长时间 运行 进程提供即时输出。