Chamando comandos shell de Ruby

votos
862

Como faço para chamar comandos shell de dentro de um programa de Ruby? Como faço para, em seguida, obter resultado destes comandos de volta para Ruby?

Publicado 05/08/2008 em 13:56
fonte usuário
Em outras línguas...                            


20 respostas

votos
1k

Esta explicação é baseada em um comentou script Ruby de um amigo meu. Se você quer melhorar o roteiro, fique à vontade para atualizá-lo no link.

Primeiro, observe que quando o Ruby chama por uma concha, que normalmente chama /bin/sh, não Bash. Alguns sintaxe Bash não é suportado pelo /bin/shem todos os sistemas.

Eis algumas formas de executar um script shell:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , Comumente chamado de acentos graves - `cmd`

    Isto é como muitas outras línguas, incluindo Bash, PHP e Perl.

    Retorna o resultado do comando shell.

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

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Built-in de sintaxe, %x( cmd )

    Seguindo o xpersonagem é um delimitador, que pode ser qualquer personagem. Se o delimitador é um dos personagens (, [, {, ou <, o literal consiste dos personagens até o delimitador de fechamento correspondente, tendo em conta pares de delimitadores aninhadas. Para todos os outros delimitadores, o literal compreende os caracteres até a próxima ocorrência do carácter delimitador. Corda interpolação #{ ... }é permitido.

    Retorna o resultado do comando shell, assim como os acentos graves.

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

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

    Executa o comando dado em um subnível.

    Retorna truese o comando foi encontrado e executado com êxito, falsecaso contrário.

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

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

    Substitui o atual processo executando o comando dado externo.

    Retorna nenhum, o processo atual é substituído e nunca continua.

    Docs: 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
    

Aqui estão alguns conselhos extra: $?, que é o mesmo que $CHILD_STATUS, acessa o status do último sistema executado comando se você usar os acentos graves, system()ou %x{}. Você pode, então, acessar o exitstatuse pidpropriedades:

$?.exitstatus

Para mais leitura, consulte:

Respondeu 05/08/2008 em 15:42
fonte usuário

votos
150

O jeito que eu gosto de fazer isso é usando o %xliteral, o que o torna fácil de usar aspas em um comando, assim (e legível!):

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

Que, neste caso, irá preencher a lista de arquivos com todos os arquivos de teste sob o diretório atual, que você pode processar conforme o esperado:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
Respondeu 05/08/2008 em 15:08
fonte usuário

votos
140

Aqui está um fluxograma com base em esta resposta . Veja também, usando scriptpara emular um terminal .

digite descrição da imagem aqui

Respondeu 19/05/2016 em 17:01
fonte usuário

votos
58

Aqui é o melhor artigo na minha opinião sobre a execução de scripts shell em Ruby: " 6 maneiras de executar comandos shell em Ruby ".

Se você só precisa obter os acentos graves uso de saída.

Eu precisava de coisas mais avançadas como STDOUT e STDERR então eu usei a gema Open4. Você tem todos os métodos explicados lá.

Respondeu 02/09/2008 em 12:05
fonte usuário

votos
31

O meu favorito é Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Respondeu 18/09/2008 em 18:47
fonte usuário

votos
23

Algumas coisas para pensar ao escolher entre estes mecanismos são:

  1. Você só quer stdout ou você precisa stderr bem? ou mesmo separados?
  2. Quão grande é a sua saída? Você quer manter todo o resultado na memória?
  3. Você quer ler alguns de sua saída, enquanto o sub-processo ainda está em execução?
  4. Você precisa de códigos de resultado?
  5. Você precisa de um objeto Ruby que representa o processo e permite que você matá-lo sob demanda?

Você pode precisar de qualquer coisa, desde backticks simples ( ``), system (), e IO.popenpara full-blown Kernel.fork/ Kernel.execcom IO.pipee IO.select.

Você também pode querer jogar o tempo limite para a mistura se um subprocesso leva muito tempo para executar.

Infelizmente, muito depende .

Respondeu 07/08/2008 em 06:10
fonte usuário

votos
19

mais uma opção:

Quando você:

  • precisa stderr, bem como stdout
  • não pode / não vai usar Open3 / Open4 (eles jogam exceções no NetBeans no meu Mac, nenhuma idéia porque)

Você pode usar o redirecionamento de shell:

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

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

A 2>&1sintaxe funciona em Linux , Mac e do Windows desde os primeiros dias de MS-DOS.

Respondeu 16/06/2010 em 03:13
fonte usuário

votos
15

Eu definitivamente não sou um especialista em Ruby, mas vou dar-lhe um tiro:

$ irb 
system "echo Hi"
Hi
=> true

Você também deve ser capaz de fazer coisas como:

cmd = 'ls'
system(cmd)
Respondeu 05/08/2008 em 14:24
fonte usuário

votos
11

As respostas acima já são bastante grande, mas eu realmente quero compartilhar o seguinte artigo resumo: " 6 maneiras de executar comandos shell em Ruby "

Basicamente, ele nos diz:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systeme $?:

system 'false' 
puts $?

Backticks ( `):

today = `date`

IO#popen:

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

Open3#popen3 - stdlib:

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

Open4#popen4 - uma jóia:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]
Respondeu 07/06/2013 em 03:07
fonte usuário

votos
7

Se você realmente precisa Bash, pela nota na "melhor" resposta.

Primeiro, observe que quando o Ruby chama por uma concha, que normalmente chama /bin/sh, não Bash. Alguns sintaxe Bash não é suportado pelo /bin/shem todos os sistemas.

Se você precisa usar Bash, coloque bash -c "your Bash-only command"dentro do seu método de chamada desejado.

quick_output = system("ls -la")

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

Testar:

system("echo $SHELL") system('bash -c "echo $SHELL"')

Ou se você estiver executando um arquivo de script existente (por exemplo script_output = system("./my_script.sh")) Rubi deve honrar o shebang, mas você sempre pode usar system("bash ./my_script.sh")para certificar-se (embora possa haver uma ligeira sobrecarga de /bin/shcorrer /bin/bash, você provavelmente não vai notar.

Respondeu 02/06/2017 em 20:14
fonte usuário

votos
7

Você também pode usar os operadores backtick ( '), semelhante ao Perl:

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

Útil se você precisar de algo simples.

Qual o método que você deseja usar depende exatamente o que você está tentando realizar; verifique a documentação para obter mais detalhes sobre os diferentes métodos.

Respondeu 05/08/2008 em 14:57
fonte usuário

votos
5

Não se esqueça do spawncomando para criar um processo em segundo plano para executar o comando especificado. Você pode mesmo esperar pela sua conclusão usando a Processclasse eo devolveu 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

O doc diz: Este método é semelhante ao #systemmas não espera para o comando ao fim.

Respondeu 04/11/2015 em 15:04
fonte usuário

votos
5

Usando as respostas aqui e ligada em resposta de Mihai, eu coloquei uma função que atenda a esses requisitos:

  1. Ordenadamente captura STDOUT e STDERR para que eles não "fuga" quando meu script é executado a partir do console.
  2. Permite que os argumentos a serem passados ​​para o shell como uma matriz, por isso não há necessidade de se preocupar em fugir.
  3. Captura o status de saída do comando por isso é claro quando ocorreu um erro.

Como um bônus, este também irá retornar STDOUT nos casos em que as saídas de comando shell com sucesso (0) e coloca nada em STDOUT. Desta forma, ele difere do systemque simplesmente retorna trueem tais casos.

Código segue. A função específica é 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"
Respondeu 21/02/2012 em 00:36
fonte usuário

votos
5

Podemos consegui-lo de várias maneiras.

Usando Kernel#exec, nada após esse comando é executado:

exec('ls ~')

utilização backticks or %x

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

Usando Kernel#systemcomando, retorna truese for bem sucedido, falsese não tiver sucesso e retorna nilse a execução do comando falhar:

system('ls ~')
=> true
Respondeu 19/02/2012 em 19:07
fonte usuário

votos
4

maneira mais fácil é, por exemplo:

reboot = `init 6`
puts reboot
Respondeu 30/03/2017 em 18:13
fonte usuário

votos
3
  • backticks método `é a mais fácil para chamar comandos shell de rubi. Ele retorna o resultado do comando shell.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
Respondeu 16/02/2017 em 09:58
fonte usuário

votos
3

Se você tem um caso mais complexo do que o caso comum (que não pode ser tratado com ``) então confira Kernel.spawn() aqui . Este parece ser o mais genérico / full-featured fornecida pelo estoque de Ruby para executar comandos externos.

Por exemplo, você pode usá-lo para:

  • criar grupos de processos (Windows)
  • redirecionar dentro, para fora, erro para arquivos / each-other.
  • set env vars, umask
  • alterar dir antes do comando execução
  • limites de recursos definidos para CPU / dados / ...
  • Fazer tudo o que pode ser feito com outras opções em outras respostas, mas com mais código.

Oficial documentação rubi tem bons exemplos suficientes.

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)
Respondeu 11/12/2015 em 14:57
fonte usuário

votos
1

Dado um comando por exemplo attrib

require 'open3'

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

Descobri que enquanto este método não é tão memorável como sistema por exemplo ( "thecommand") ou thecommand em acentos graves, uma coisa boa sobre este método em comparação com outros métodos .. é que por exemplo backticks parece não deixe-me 'puts 'o comando Eu corro / store o comando eu quero correr em uma variável, e system ( 'thecommand') não parece deixar-me obter a saída. Considerando este método me permite fazer as duas coisas, e isso me deixa stdin acesso, stdout e stderr de forma independente.

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

Respondeu 19/12/2017 em 05:54
fonte usuário

votos
0

Não é realmente uma resposta, mas talvez alguém vai encontrar este útil, e sua relação a isso.

Ao usar TK GUI no Windows, e u precisa chamar comandos shell de rubyw, u terá sempre uma janela cmd chato aparecendo por menos de um segundo.

Para evitar isso u pode usar

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

ou

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

Ambos irão armazenar a saída ipconfig do interior 'log.txt', mas sem janelas virão para cima.

U vai precisar require 'win32ole'dentro de seu script.

system(), exec()E spawn()tudo vai aparecer essa janela irritante quando usando TK e rubyw.

Respondeu 05/07/2018 em 12:55
fonte usuário

votos
-1

Aqui está um fresco que eu uso em um script Ruby on OS X (para que eu possa começar um script e obter uma atualização, mesmo depois de alternar longe da janela):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Respondeu 14/10/2014 em 21:12
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more