首页 文章

从Ruby调用shell命令

提问于
浏览
930

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

20 回答

  • 181

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

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

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

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

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

    返回shell命令的结果 .

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

    value = `echo 'hi'`
    value = `#{cmd}`
    
    • 内置语法, %x( cmd )

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

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

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

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
    • Kernel#system

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

    如果找到并成功运行命令,则返回 true ,否则返回 false .

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

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
    • Kernel#exec

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

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

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

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

    这里有一些额外的建议: $? ,与 $CHILD_STATUS 相同,如果你使用反引号, system()%x{} ,则访问最后一个系统执行命令的状态 . 然后,您可以访问 exitstatuspid 属性:

    $?.exitstatus
    

    如需更多阅读,请参阅

  • -1
    • backticks`方法是从ruby调用shell命令最简单的方法 . 它返回shell命令的结果 .
    url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
  • 1199

    给出命令,例如attrib

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

    我发现虽然这种方法不像以下那样令人难忘 . 系统(“命令”)或反引号中的命令,与其他方法相比,这种方法的一个好处是...反引号似乎没有让我'put'命令我运行/存储我想要在变量中运行的命令,而system(“thecommand”)似乎不让我得到输出 . 虽然这个方法允许我做这两件事,但它让我可以独立访问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

  • 59

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

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

    exec('ls ~')
    

    使用 backticks or %x

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

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

    system('ls ~')
    => true
    
  • 23

    还有一个选择:

    当你:

    • 需要stderr以及stdout

    • 可以't/won' t使用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上工作 .

  • 20

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

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

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

  • 18

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

    • 整齐地捕获STDOUT和STDERR,以便在从控制台运行脚本时它们不会"leak" .

    • 允许将参数作为数组传递给shell,因此无需担心转义 .

    • 捕获命令的退出状态,以便在发生错误时清除 .

    作为奖励,在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"
    
  • 5

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

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

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

    directorylist.each do |filename|
      filename.chomp!
      # work with file
    end
    
  • 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 但它不等待命令完成 .

  • 7

    不是一个真正的答案,但也许有人会觉得这很有用,而且就此而言 .

    使用TK GUI时在Windows上,你需要从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() 都会弹出那个烦人的窗口 .

  • 12

    我最喜欢的是Open3

    require "open3"
    
      Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
    
  • 1

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

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

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

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

  • 3

    上面的答案已经非常好了,但我真的想分享以下摘要文章:“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>]
    
  • 154

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

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

    如果你真的需要Bash,请按照“最佳”答案中的说明 .

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

    如果您需要使用Bash,请在所需的调用方法中插入 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"')

    或者,如果您正在运行现有的脚本文件(例如 script_output = system("./my_script.sh") ),Ruby应该尊重shebang,但您可以始终使用 system("bash ./my_script.sh") 来确保(尽管 /bin/sh 运行 /bin/bash 可能会有轻微的开销,您可能不会注意到 .

  • 5

    最简单的方法是,例如:

    reboot = `init 6`
    puts reboot
    
  • 10

    这是基于this answer的流程图 . 另见,using script to emulate a terminal .

    enter image description here

  • 3

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

    • 你只想要stdout还是你还需要stderr?甚至分开了?

    • 你的产量有多大?你想把整个结果保存在记忆中吗?

    • 在子进程仍在运行时,是否要读取一些输出?

    • 您需要结果代码吗?

    • 您是否需要一个代表该进程的ruby对象并允许您按需杀死它?

    你可能需要从简单的反引号(``),system()和 IO.popen 到完整的 Kernel.fork / Kernel.exec 以及 IO.pipeIO.select .

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

    不幸的是,它非常多 depends .

  • 6

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

    $ irb 
    system "echo Hi"
    Hi
    => true
    

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

    cmd = 'ls'
    system(cmd)
    
  • -2

    如果你的案例比普通案例更复杂(无法用 ```` 处理),请查看 Kernel.spawn() here . 这似乎是 stock Ruby 提供的执行外部命令的最通用/全功能 .

    例如 . 你可以用它来:

    • 创建进程组(Windows)

    • 重定向,输出,错误到文件/每个其他 .

    • 设置env vars,umask

    • 在执行命令之前更改dir

    • 设置CPU / data /的资源限制

    • 在其他答案中,可以使用其他选项完成所有操作,但需要更多代码 .

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

    env: hash
      name => val : set the environment variable
      name => nil : unset the environment variable
    command...:
      commandline                 : command line string which is passed to the standard shell
      cmdname, arg1, ...          : command name and one or more arguments (no shell)
      [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
    options: hash
      clearing environment variables:
        :unsetenv_others => true   : clear environment variables except specified by env
        :unsetenv_others => false  : dont clear (default)
      process group:
        :pgroup => true or 0 : make a new process group
        :pgroup => pgid      : join to specified process group
        :pgroup => nil       : dont change the process group (default)
      create new process group: Windows only
        :new_pgroup => true  : the new process is the root process of a new process group
        :new_pgroup => false : dont create a new process group (default)
      resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
        :rlimit_resourcename => limit
        :rlimit_resourcename => [cur_limit, max_limit]
      current directory:
        :chdir => str
      umask:
        :umask => int
      redirection:
        key:
          FD              : single file descriptor in child process
          [FD, FD, ...]   : multiple file descriptor in child process
        value:
          FD                        : redirect to the file descriptor in parent process
          string                    : redirect to file with open(string, "r" or "w")
          [string]                  : redirect to file with open(string, File::RDONLY)
          [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
          [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
          [:child, FD]              : redirect to the redirected file descriptor
          :close                    : close the file descriptor in child process
        FD is one of follows
          :in     : the file descriptor 0 which is the standard input
          :out    : the file descriptor 1 which is the standard output
          :err    : the file descriptor 2 which is the standard error
          integer : the file descriptor of specified the integer
          io      : the file descriptor specified as io.fileno
      file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
        :close_others => false : inherit fds (default for system and exec)
        :close_others => true  : dont inherit (default for spawn and IO.popen)
    

相关问题