如何从Ruby程序内部调用shell命令?然后如何将这些命令的输出返回到Ruby中?


当前回答

给定像attrib这样的命令:

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

我发现,虽然这种方法不像

system("thecommand")

or

`thecommand`

在反引号中,与其他方法相比,此方法的一个优点是 反勾号似乎不让我把我运行的命令/存储我想要运行的命令在一个变量中,而system(" command")似乎不让我得到输出,而这个方法让我做这两件事,它让我访问stdin, stdout和stderr独立。

参见“在ruby中执行命令”和ruby的Open3文档。

其他回答

我最喜欢的是Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

你也可以使用反勾操作符('),类似于Perl:

directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory

如果你需要简单的东西,很方便。

你想要使用哪种方法取决于你想要完成什么;有关不同方法的更多细节,请查看文档。

使用这里的答案和Mihai的答案链接,我组合了一个满足这些要求的函数:

整洁地捕获STDOUT和STDERR,因此当我的脚本从控制台运行时,它们不会“泄漏”。 允许参数作为数组传递给shell,因此不需要担心转义。 捕获命令的退出状态,以便在发生错误时显示出来。

作为奖励,当shell命令成功退出(0)并将任何内容放在STDOUT上时,该命令还将返回STDOUT。在这种情况下,它与system不同,后者在这种情况下只返回true。

代码如下。具体的函数是system_quiet:

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
    err = stderr.gets(nil)
    out = stdout.gets(nil)
    [stdin, stdout, stderr].each{|stream| stream.send('close')}
    exit_status = wait_thread.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"

您可以使用如下格式方法打印一些信息:

puts format('%s', `ps`)
puts format('%d MB', (`ps -o rss= -p #{Process.pid}`.to_i / 1024))

我喜欢的方法是使用%x文字,这使得在命令中使用引号很容易(而且易读!),如下所示:

directorylist = %x[find . -name '*test.rb' | sort]

在这种情况下,它将填充当前目录下的所有测试文件的文件列表,您可以按照预期进行处理:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end