从Ruby调用shell命令

时间:2008-08-05 12:56:53

标签: ruby shell interop

如何从Ruby程序内部调用shell命令?然后我如何从这些命令输出回Ruby?

20 个答案:

答案 0 :(得分:1238)

此解释基于我朋友的评论Ruby script。如果您想改进脚本,请随时在链接上更新它。

首先,请注意当Ruby调用shell时,它通常会调用/bin/sh而不是 Bash。所有系统上/bin/sh都不支持某些Bash语法。

以下是执行shell脚本的方法:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#`,通常称为反引号 - `cmd`

    这与许多其他语言一样,包括Bash,PHP和Perl。

    返回shell命令的结果。

    文档:http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. 内置语法{​​{1}}

    %x( cmd )字符后面是分隔符,可以是任何字符。 如果分隔符是字符x([{之一, 文字由直到匹配的结束分隔符的字符组成, 考虑嵌套分隔符对。对于所有其他分隔符, 文字包括直到下一次出现的字符 分隔符。允许字符串插值<

    返回shell命令的结果,就像反引号一样。

    文档:http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    #{ ... }
  3. value = %x( echo 'hi' ) value = %x[ #{cmd} ]

    在子shell中执行给定的命令。

    如果找到并成功运行命令,则返回Kernel#system,否则返回true

    文档:http://ruby-doc.org/core/Kernel.html#method-i-system

    false
  4. wasGood = system( "echo 'hi'" ) wasGood = system( cmd )

    通过运行给定的外部命令替换当前进程。

    返回none,当前进程被替换并且永远不会继续。

    文档:http://ruby-doc.org/core/Kernel.html#method-i-exec

    Kernel#exec
  5. 这是一些额外的建议: exec( "echo 'hi'" ) exec( cmd ) # Note: this will never be reached because of the line above $?相同,如果您使用反引号$CHILD_STATUSsystem(),则会访问上次系统执行命令的状态。 然后,您可以访问%x{}exitstatus属性:

    pid

    如需更多阅读,请参阅:

答案 1 :(得分:213)

以下是基于this answer的流程图。另请参阅using script to emulate a terminal

enter image description here

答案 2 :(得分:156)

我喜欢这样做的方法是使用%x文字,这样可以轻松(并且可读!)在命令中使用引号,如下所示:

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

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

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

答案 3 :(得分:60)

以下是我在Ruby中运行shell脚本的最佳文章:“6 Ways to Run Shell Commands in Ruby”。

如果你只需要输出使用反引号。

我需要更高级的东西,如STDOUT和STDERR,所以我使用了Open4 gem。你有解释的所有方法。

答案 4 :(得分:32)

我最喜欢的是Open3

  require "open3"

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

答案 5 :(得分:23)

在选择这些机制时需要考虑的一些事项是:

  1. 你只想要stdout还是你呢? 还需要stderr吗?甚至 分开了?
  2. 你的产量有多大?你想要 将整个结果保存在记忆中?
  3. 你想读一些你的吗? 子进程静止时输出 运行?
  4. 您需要结果代码吗?
  5. 你需要一个红宝石对象吗? 代表过程并让你 按需杀死它?
  6. 你可能需要从简单的反引号(``),system()和IO.popenKernel.fork / Kernel.exec IO.pipe和{{1}的完整版本。 }}

    如果子进程执行时间太长,您可能还希望将超时投入混合。

    不幸的是,它非常 取决于

答案 6 :(得分:20)

我绝对不是Ruby专家,但我会试一试:

$ irb 
system "echo Hi"
Hi
=> true

您还应该能够执行以下操作:

cmd = 'ls'
system(cmd)

答案 7 :(得分:20)

还有一个选择:

当你:

  • 需要stderr以及stdout
  • 不能/不会使用Open3 / Open4(他们在Mac上的NetBeans中抛出异常,不明白为什么)

您可以使用shell重定向:

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

自MS-DOS早期以来,2>&1语法适用于Linux,Mac和Windows

答案 8 :(得分:13)

上面的答案已经非常好了,但我真的想分享以下摘要文章:“6 Ways to Run Shell Commands in Ruby

基本上,它告诉我们:

Kernel#exec

exec 'echo "hello $HOSTNAME"'

system$?

system 'false' 
puts $?

反引号(`):

today = `date`

IO#popen

IO.popen("date") { |f| puts f.gets }

Open3#popen3 - stdlib:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 - 宝石:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

答案 9 :(得分:10)

如果你真的需要Bash,请按照&#34; best&#34;答案。

  

首先,请注意当Ruby调用shell时,它通常会调用.K3D而不是 Bash。所有系统上的/bin/sh都不支持某些Bash语法。

如果您需要使用Bash,请在您想要的调用方法中插入/bin/sh

bash -c "your Bash-only command"

quick_output = system("ls -la")

测试:

quick_bash = system("bash -c 'ls -la'")

或者如果您正在运行现有的脚本文件(例如 system("echo $SHELL") system('bash -c "echo $SHELL"') ),Ruby 尊重shebang,但您可以始终使用script_output = system("./my_script.sh")来确保(尽管可能system("bash ./my_script.sh")运行/bin/sh只是一点点开销,你可能不会注意到。

答案 10 :(得分:7)

你也可以使用反引号运算符(`),类似于Perl:

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

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

您想要使用哪种方法取决于您要完成的内容;检查文档以获取有关不同方法的更多详细信息。

答案 11 :(得分:6)

最简单的方法是,例如:

reboot = `init 6`
puts reboot

答案 12 :(得分:5)

不要忘记spawn命令来创建执行指定命令的后台进程。您甚至可以使用Process类和返回的pid

等待其完成
pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

该文档说:此方法与#system类似,但它不会等待命令完成。

答案 13 :(得分:5)

使用这里的答案并在Mihai的答案中链接,我整理了一个满足这些要求的功能:

  1. 巧妙地捕获STDOUT和STDERR,这样当我的脚本从控制台运行时它们就不会“泄漏”。
  2. 允许参数作为数组传递给shell,因此无需担心转义。
  3. 捕获命令的退出状态,以便在发生错误时清除。
  4. 作为奖励,如果shell命令成功退出(0)并将任何内容放在STDOUT上,这个也将返回STDOUT。以这种方式,它与system不同,true在这种情况下只返回system_quietly

    代码如下。具体功能是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"

    {{1}}

答案 14 :(得分:5)

我们可以通过多种方式实现它。

使用Kernel#exec,执行此命令后不执行任何操作:

exec('ls ~')

使用backticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

使用Kernel#system命令,如果成功则返回true,如果不成功则返回false,如果命令执行失败则返回nil

system('ls ~')
=> true

答案 15 :(得分:3)

如果您的案件比普通案件更复杂(无法使用import java.util.Scanner; public class TryDouble { public static void main(String [] args){ Scanner jin = new Scanner(System.in); double a = jin.nextDouble(); double b = jin.nextDouble(); double c = jin.nextDouble(); System.out.println(a + b + c); } } 处理),请查看Exception in thread "main" java.util.InputMismatchException at java.util.Scanner.throwFor(Unknown Source) at java.util.Scanner.next(Unknown Source) at java.util.Scanner.nextDouble(Unknown Source) at TryDouble.main(TryDouble.java:6) here。这似乎是 stock Ruby 为执行外部命令而提供的最通用/全功能。

E.g。你可以用它来:

  • 创建流程组(Windows)
  • 将输入,输出,错误重定向到文件/每个其他。
  • 设置env vars,umask
  • 在执行命令
  • 之前更改dir
  • 设置CPU / data /...
  • 的资源限制
  • 在其他答案中,可以使用其他选项完成所有操作,但需要更多代码。

官方ruby documentation有足够好的例子。

``

答案 16 :(得分:3)

  • 反引号`方法是从ruby调用shell命令最简单的方法。它返回shell命令的结果。

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    

答案 17 :(得分:2)

给出命令,例如attrib

require 'open3'

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

我发现虽然这种方法不像以下方法那样令人难忘。系统(&#34;命令&#34;)或反引号中的命令,与其他方法相比,这种方法的一个好处是...  反叛似乎没有让我放弃&#39;命令我运行/存储我想在变量中运行的命令,系统(&#34;命令&#34;)似乎不让我得到输出。虽然这个方法允许我做这两件事,但它让我可以独立访问stdin,stdout和stderr。

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html

答案 18 :(得分:1)

并不是真正的答案,但也许有人会发现它有用,并且与此有关。

在Windows上使用TK GUI时,您需要从rubyw调用shell命令,您总是会弹出一个烦人的cmd窗口,时间不到一秒钟。

为避免这种情况,您可以使用

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

两者都将ipconfig的输出存储在'log.txt'内,但是不会出现任何窗口。

您需要在脚本中require 'win32ole'

当使用TK和rubyw时,

system()exec()spawn()都会弹出该烦人的窗口。

答案 19 :(得分:-1)

这是我在OS X上的ruby脚本中使用的一个很酷的一个(这样我即使在从窗口切换后也可以启动脚本并获得更新):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )