Pergunta

Como faço para chamar comandos do shell de dentro de um programa Ruby?Como faço para, em seguida, obter a saída destes comandos de volta em Ruby?

Foi útil?

Solução

Esta explicação é baseada em um comentou Ruby script a partir de um amigo meu.Se você quer melhorar o script, sinta-se livre para atualizar o link.

Primeiro, observe que, quando Ruby chama a uma concha, normalmente chamadas de /bin/sh, não Bash.Alguns Bash sintaxe não é suportada pelo /bin/sh em todos os sistemas.

Aqui estão maneiras de executar um shell script:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , comumente chamado de aspas invertidas – `cmd`

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

    Retorna o resultado da shell de comando.

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

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Construído-em sintaxe, %x( cmd )

    A seguir o x personagem é um delimitador, que pode ser qualquer personagem.Se o delimitador é um dos personagens (, [, {, ou <, o literal é composto dos caracteres até o correspondente delimitador de fechamento, tendo em conta aninhadas delimitador de pares.Para todos os outros delimitadores, o literal compreende os caracteres até a próxima ocorrência do delimitador de caracteres.Cadeia de interpolação #{ ... } é permitida.

    Retorna o resultado da shell de comando, assim como as aspas invertidas.

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

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

    Executa o comando especificado em um sub-shell.

    Retorna true se o comando foi encontrado e executado com êxito, false caso 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 de execução de determinado comando 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
    

Eis algumas recomendações:$?, que é o mesmo que $CHILD_STATUS, acessa o status da última sistema de comando executada se você usar as aspas invertidas, system() ou %x{}.Em seguida, você pode acessar o exitstatus e pid propriedades:

$?.exitstatus

Para mais leitura ver:

Outras dicas

Aqui está um fluxograma com base no esta resposta.Ver também, usando script para emular um terminal.

enter image description here

Do jeito que eu gosto de fazer isso é usando o %x literal, o que torna mais fácil (e legível!) usar aspas em um comando, como em:

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

O que, neste caso, vai preencher arquivo lista com todos os arquivos de teste no diretório atual, o que você pode processar conforme o esperado:

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

Aqui está o melhor artigo no meu parecer sobre a execução de shell scripts em Ruby:"6 Maneiras de Executar Comandos de Shell em Ruby".

Se você só precisa obter a saída use aspas invertidas.

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

O meu favorito é Open3

  require "open3"

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

Algumas coisas a considerar ao escolher entre esses mecanismos são:

  1. Você só quer stdout ou você precisa stderr bem?ou até separados?
  2. Quão grande é a sua saída?Você quer para armazenar todo o resultado na memória?
  3. Você deseja ler alguns de seus de saída, enquanto o subprocesso é ainda execução?
  4. Você precisa de códigos de resultado?
  5. Você precisa de um rubi objeto que representa o processo e permite que você mate-o, em demanda?

Você pode precisar de qualquer coisa, desde simples aspas invertidas (`), system(), e IO.popen para full-blown Kernel.fork/Kernel.exec com IO.pipe e IO.select.

Você também pode querer jogar tempos de espera para a mistura se um subprocesso leva muito tempo para executar.

Infelizmente, é muito depende.

Mais uma opção:

Quando você:

  • precisa stderr, bem como stdout
  • não pode/não usar Open3/Open4 (eles lançam exceções no NetBeans no meu Mac, nenhuma idéia de por que)

Você pode usar o shell de redirecionamento:

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

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

O 2>&1 sintaxe funciona através de Linux, Mac e Windows desde os primeiros dias do MS-DOS.

Eu, definitivamente, não é um Rubi especialista, mas eu vou dar um tiro:

$ irb 
system "echo Hi"
Hi
=> true

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

cmd = 'ls'
system(cmd)

As respostas acima são já bastante grande, mas eu realmente quero compartilhar o resumo seguinte artigo:"6 Maneiras de Executar Comandos de Shell em Ruby"

Basicamente, ele nos diz:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system e $?:

system 'false' 
puts $?

Aspas invertidas (`):

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>]

Se você realmente precisa de Bash, de acordo com a nota no "melhor resposta".

Primeiro, observe que, quando Ruby chama a uma concha, normalmente chamadas de /bin/sh, não Bash.Alguns Bash sintaxe não é suportada pelo /bin/sh em todos os sistemas.

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

quick_output = system("ls -la")

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

Para 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 honra a tudo, mas você sempre pode usar system("bash ./my_script.sh") para certificar-se (embora possa haver uma ligeira sobrecarga de /bin/sh execução /bin/bash, você provavelmente não vai notar.

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

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

Útil se você precisa de algo simples.

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

a maneira mais fácil é, por exemplo:

reboot = `init 6`
puts reboot

Usando as respostas aqui e vinculada Mihai a resposta, 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 a shell como uma matriz, de modo que não há necessidade de se preocupar em escapar.
  3. Capta o estado de saída do comando para é claro, quando ocorreu um erro.

Como um bônus, isso também vai retornar STDOUT em casos onde o shell de comando é encerrado com êxito (0) e coloca qualquer coisa no STDOUT.Desta forma, é diferente do system, que simplesmente retorna true em tais casos.

O código a seguir.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"

Podemos alcançá-lo de várias maneiras.

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

exec('ls ~')

Usando backticks or %x

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

Usando Kernel#system comando retorna true se for bem-sucedido, false se não tiver êxito e devolve nil se a execução do comando falhar:

system('ls ~')
=> true

Não se esqueça de o spawn o comando para criar um processo de plano de fundo a executar o comando especificado.Você pode mesmo esperar para a sua realização, utilizando o Process classe e devolvido 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 médico diz:Este método é semelhante ao #system mas não espere para concluir o comando.

Se você tem um caso mais complexo que o caso comum (que não pode ser tratada com ``), em seguida, confira Kernel.spawn() aqui.Este parece ser o mais genérico/completo fornecido por estoque Ruby para executar comandos externos.

E. g.você pode usá-lo para:

  • criar grupos de processos do Windows (Windows)
  • redirecionar in, saída de erro para arquivos/cada-outros.
  • conjunto env vars, umask
  • alterar diretório antes de executar o comando
  • definir limites de recursos para CPU/dados/...
  • Fazer tudo o que pode ser feito com outras opções em outras respostas, mas com mais código.

Oficial ruby documentação 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)
  • aspas invertidas ` método é o mais fácil para chamar comandos do shell do ruby.Ele retorna o resultado do comando do shell.

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

Dado um comando por exemplo attrib

require 'open3'

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

Eu encontrei que embora este método não é tão memorável como por exemplo,system("thecommand") ou thecommand em aspas invertidas, uma coisa boa sobre este método em comparação com outros métodos..é e.g.aspas para traz não parece deixar-me 'coloca' eu executar o comando / arquivo o comando que deseja executar em uma variável, e sistema("thecommand") não parece deixe-me chegar a saída.Considerando que este método permite-me fazer ambas as coisas, e ele me permite acessar stdin, 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

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

Quando usando TK GUI no Windows, e a necessidade de u para chamar comandos do shell de rubyw, vc sempre vai ter um chato cmd janela aparecendo para menos de um segundo.

Para evitar este u pode usar o

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 ipconfig de saída do interior "log.txt', mas não o windows vai aparecer.

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

system(), exec() e spawn() será que todos os pop-up irritante janela quando usando TK e rubyw.

Aqui está um mais legal que eu uso em um script do ruby no mac OS X (para que eu possa iniciar um script e obter uma atualização, mesmo após a alternância de fora da janela):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top