guide - Chamando comandos de shell do Ruby




ruby reference guide (14)

Como chamo comandos de shell de dentro de um programa Ruby? Como faço para obter a saída desses comandos novamente para o Ruby?


A maneira mais fácil é, por exemplo:

reboot = `init 6`
puts reboot

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

  1. Você só quer stdout ou precisa de stderr também? ou mesmo separados?
  2. Qual é o tamanho da sua saída? Deseja manter o resultado inteiro na memória?
  3. Deseja ler parte de sua saída enquanto o subprocesso ainda está em execução?
  4. Você precisa de códigos de resultado?
  5. Você precisa de um objeto ruby ​​que represente o processo e permita matá-lo sob demanda?

Você pode precisar de qualquer coisa, desde simples backticks (``), system () e IO.popen a Kernel.fork / Kernel.exec com IO.pipe e IO.select .

Você também pode lançar tempos limite na mistura se um subprocesso demorar muito para ser executado.

Infelizmente, depende muito.



Aqui está um que eu uso em um script ruby ​​no OS X (para que eu possa iniciar um script e obter uma atualização mesmo depois de sair da janela):

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

Dado um comando, por exemplo, attrib

require 'open3'

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

Descobri que, embora esse método não seja tão memorável como, por exemplo, sistema ("comando") ou comando em backticks, uma coisa boa sobre esse método em comparação com outros métodos .. é, por exemplo, que backticks parecem não me deixar colocar 'o comando que eu corro / armazeno o comando que eu quero executar em uma variável, e o sistema ("thecommand") parece não me deixar obter a saída. Enquanto esse método me permite fazer as duas coisas e permite acessar stdin, stdout e stderr independentemente.

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


Definitivamente, não sou especialista em Ruby, mas vou tentar:

$ irb 
system "echo Hi"
Hi
=> true

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

cmd = 'ls'
system(cmd)

Mais uma opção:

Quando você:

  • precisa stderr, bem como stdout
  • não pode / não usa o Open3 / Open4 (eles lançam exceções no NetBeans no meu Mac, não sei por que)

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 sintaxe 2>&1 funciona no Linux , Mac e Windows desde os primeiros dias do MS-DOS.


Não é realmente uma resposta, mas talvez alguém ache isso útil, e está relacionado a isso.

Ao usar a TK GUI no Windows, e você precisa chamar comandos de shell do rubyw, sempre haverá uma janela irritante do cmd aparecendo por menos de um segundo.

Para evitar isso, você pode usar

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

ou

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

Ambos armazenam a saída do ipconfig dentro de 'log.txt', mas nenhuma janela será exibida.

Você precisará require 'win32ole' dentro do seu script.

system() , exec() e spawn() aparecerão nessa janela irritante ao usar TK e rubyw.


O jeito que eu gosto de fazer isso é usar o %x literal, o que facilita (e é legível!) Usar aspas em um comando, da seguinte maneira:

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

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

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

O meu favorito é o Open3

  require "open3"

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

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

Primeiro, observe que, quando Ruby chama um shell, ele normalmente chama /bin/sh , não Bash. Algumas sintaxas do Bash não são suportadas pelo /bin/sh em todos os sistemas.

Se você precisar usar o Bash, insira bash -c "your Bash-only command" dentro do 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") ), o Ruby deve respeitar o shebang, mas você sempre pode usar system("bash ./my_script.sh") para garantir ( embora possa haver uma pequena sobrecarga em /bin/sh running /bin/bash , você provavelmente não notará.


Se você tiver um caso mais complexo que o caso comum (que não pode ser tratado com `` ), verifique o Kernel.spawn() here . Este parece ser o mais genérico / completo fornecido pelo estoque Ruby para executar comandos externos.

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

  • criar grupos de processos (Windows)
  • redirecionar dentro, fora, erro para arquivos / uns aos outros.
  • definir env vars, umask
  • alterar dir antes de executar o comando
  • definir limites de recursos para CPU / dados / ...
  • Faça tudo o que puder ser feito com outras opções em outras respostas, mas com mais código.

A here oficial do here tem bons exemplos.

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)

Você também pode usar os operadores de backtick (`), semelhantes ao Perl:

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

Útil se você precisar de algo simples.

Qual método você deseja usar depende exatamente do que você está tentando realizar; consulte os documentos para obter mais detalhes sobre os diferentes métodos.


  • O método backticks `é o mais fácil de chamar comandos shell do ruby. Retorna o resultado do comando shell.

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




interop