如果我调用一个命令使用内核#系统在Ruby中,我如何得到它的输出?

system("ls")

当前回答

虽然使用反勾号或popen通常是您真正想要的,但它实际上并不能回答所提出的问题。可能有捕获系统输出的正当理由(可能是为了自动化测试)。我在谷歌上搜索了一下,找到了一个答案,我想把它贴在这里,以造福他人。

因为我需要这个来测试我的例子使用块设置来捕获标准输出,因为实际的系统调用隐藏在被测试的代码中:

require 'tempfile'

def capture_stdout
  stdout = $stdout.dup
  Tempfile.open 'stdout-redirect' do |temp|
    $stdout.reopen temp.path, 'w+'
    yield if block_given?
    $stdout.reopen stdout
    temp.read
  end
end

该方法使用tempfile存储实际数据来捕获给定块中的任何输出。使用示例:

captured_content = capture_stdout do
  system 'echo foo'
end
puts captured_content

您可以将系统调用替换为任何内部调用系统的东西。如果需要,还可以使用类似的方法来捕获stderr。

其他回答

最方便的方法是:

    stdout_str, stderr_str, status = Open3.capture3(cmd)
    puts "exit status: #{status.exitstatus} stdout: #{stdout_str}"

请注意,所有向系统传递包含用户提供值的字符串的解决方案,%x[]等都是不安全的!不安全实际上意味着:用户可以触发代码在上下文中运行,并具有程序的所有权限。

据我所知,只有系统和Open3。popen3在Ruby 1.8中提供了一个安全/转义的变体。在Ruby 1.9中,IO::popen也接受数组。

只需将每个选项和参数作为数组传递给其中一个调用。

如果你不仅需要退出状态,还需要结果,你可能会使用Open3.popen3:

require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value

注意,块表单将自动关闭stdin, stdout和stderr-否则它们必须显式关闭。

更多信息:在Ruby中形成卫生shell命令或系统调用

只是为了记录,如果你想要(输出和操作结果),你可以这样做:

output=`ls no_existing_file` ;  result=$?.success?

如果你想使用Kernel#系统将输出重定向到一个文件,你可以这样修改描述符:

以追加模式将stdout和stderr重定向到文件(/tmp/log):

系统(ls状态”,:= > [/ tmp /日志,' '],:呃= > [/ tmp /日志,' '])

对于长时间运行的命令,这将实时存储输出。您还可以使用IO存储输出。管道和重定向它从内核#系统。

如果你需要转义参数,在Ruby 1.9 IO中。Popen也接受数组:

p IO.popen(["echo", "it's escaped"]).read

在早期版本中,你可以使用Open3.popen3:

require "open3"

Open3.popen3("echo", "it's escaped") { |i, o| p o.read }

如果你还需要传递stdin,这应该在1.9和1.8中都有效:

out = IO.popen("xxd -p", "r+") { |io|
    io.print "xyz"
    io.close_write
    io.read.chomp
}
p out # "78797a"