Obtendo a saída de chamadas do sistema () em Ruby

Se eu chamar um comando usando o sistema Kernel # em Ruby, como obtenho sua saída?

system("ls") 

Eu gostaria de expandir e esclarecer a resposta do caos um pouco.

Se você cercar seu comando com backticks, então você não precisa (explicitamente) chamar system (). Os backticks executam o comando e retornam a saída como uma string. Você pode então atribuir o valor a uma variável assim:

 output = `ls` p output 

ou

 printf output # escapes newline chars 

Esteja ciente de que todas as soluções em que você passa uma string contendo valores fornecidos pelo usuário para o system , %x[] etc. são inseguras! Inseguro na verdade significa: o usuário pode acionar o código para ser executado no contexto e com todas as permissions do programa.

Tanto quanto eu posso dizer que apenas o system e o Open3.popen3 fornecem uma variante segura / de escape no Ruby 1.8. Em Ruby 1.9 IO::popen também aceita um array.

Basta passar todas as opções e argumentos como uma matriz para uma dessas chamadas.

Se você não precisa apenas do status de saída, mas também do resultado, você provavelmente quer usar o Open3.popen3 :

 require 'open3' stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username']) stdout.gets(nil) stdout.close stderr.gets(nil) stderr.close exit_code = wait_thr.value 

Observe que o formulário de bloqueio fechará automaticamente stdin, stdout e stderr; caso contrário, eles teriam que ser fechados explicitamente .

Mais informações aqui: Formando comandos sanitários ou chamadas do sistema em Ruby

Apenas para o registro, se você quer tanto (resultado de saída e operação) você pode fazer:

 output=`ls no_existing_file` ; result=$?.success? 

Você pode usar system () ou% x [] dependendo do tipo de resultado que você precisa.

system () retornando true se o comando foi encontrado e executado com sucesso, caso contrário, false.

 >> s = system 'uptime' 10:56 up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14 => true >> s.class => TrueClass >> $?.class => Process::Status 

% x [..] por outro lado salva os resultados do comando como uma string:

 >> result = %x[uptime] => "13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n" >> p result "13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n" >> result.class => String 

A postagem no blog de Jay Fields explica em detalhes as diferenças entre o uso do sistema, exec e% x [..].

A maneira simples de fazer isso corretamente e com segurança é usar Open3.capture2() , Open3.capture2e() ou Open3.capture3() .

O uso dos backticks de ruby ​​e seu %x alias NÃO É SEGURO SOB NENHUMA CIRCUNSTÂNCIA se usado com dados não confiáveis. É PERIGOSO , simples e simples:

 untrusted = "; date; echo" out = `echo #{untrusted}` # BAD untrusted = '"; date; echo"' out = `echo "#{untrusted}"` # BAD untrusted = "'; date; echo'" out = `echo '#{untrusted}'` # BAD 

A function do system , ao contrário, escapa dos argumentos corretamente se usada corretamente :

 ret = system "echo #{untrusted}" # BAD ret = system 'echo', untrusted # good 

O problema é que ele retorna o código de saída ao invés da saída, e capturar o último é complicado e confuso.

A melhor resposta neste tópico até agora menciona o Open3, mas não as funções que são mais adequadas para a tarefa. Open3.capture2 , capture2e e capture3 funcionam como system , mas retorna dois ou três argumentos:

 out, err, st = Open3.capture3("echo #{untrusted}") # BAD out, err, st = Open3.capture3('echo', untrusted) # good out_err, st = Open3.capture2e('echo', untrusted) # good out, st = Open3.capture2('echo', untrusted) # good p st.exitstatus 

Outro menciona IO.popen() . A syntax pode ser desajeitada no sentido de querer uma matriz como input, mas também funciona:

 out = IO.popen(['echo', untrusted]).read # good 

Por conveniência, você pode embrulhar Open3.capture3() em uma function, por exemplo:

 # # Returns stdout on success, false on failure, nil on error # def syscall(*cmd) begin stdout, stderr, status = Open3.capture3(*cmd) status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol rescue end end 

Exemplo:

 p system('foo') p syscall('foo') p system('which', 'foo') p syscall('which', 'foo') p system('which', 'which') p syscall('which', 'which') 

Produz o seguinte:

 nil nil false false /usr/bin/which <— stdout from system('which', 'which') true <- p system('which', 'which') "/usr/bin/which" <- p syscall('which', 'which') 

Você usa backticks:

 `ls` 

Outra maneira é:

 f = open("|ls") foo = f.read() 

Note que é o caractere “pipe” antes de “ls” em aberto. Isso também pode ser usado para alimentar dados na input padrão do programa, bem como ler sua saída padrão.

Se você precisar escaping dos argumentos, no Ruby 1.9 IO.popen também aceita um array:

 p IO.popen(["echo", "it's escaped"]).read 

Nas versões anteriores, você pode usar o Open3.popen3 :

 require "open3" Open3.popen3("echo", "it's escaped") { |i, o| p o.read } 

Se você também precisa passar stdin, isso deve funcionar em 1.9 e 1.8:

 out = IO.popen("xxd -p", "r+") { |io| io.print "xyz" io.close_write io.read.chomp } p out # "78797a" 

Eu achei que o seguinte é útil se você precisar do valor de retorno:

 result = %x[ls] puts result 

Eu queria especificamente listar os pids de todos os processos Java na minha máquina e usei isto:

 ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs] 

Como Simon Hürlimann já explicou , o Open3 é mais seguro que backticks etc.

 require 'open3' output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read } 

Observe que o formulário de bloqueio fechará automaticamente stdin, stdout e stderr; caso contrário, eles teriam que ser fechados explicitamente .

Se você quiser que a saída seja redirecionada para um arquivo usando o Kernel#system , você pode modificar descritores como este:

redirect stdout e stderr para um arquivo (/ tmp / log) no modo append:

system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])

Para um comando de longa duração, isso armazenará a saída em tempo real. Você também pode armazenar a saída usando um IO.pipe e redirecioná-lo do sistema Kernel #.

Embora o uso de backticks ou popen geralmente seja o que você realmente quer, ele não responde de fato à pergunta. Pode haver razões válidas para capturar a saída do system (talvez para testes automatizados). Um pouco de Googling apareceu uma resposta que pensei que postaria aqui para o benefício dos outros.

Desde que eu precisei disso para testar meu exemplo, usa uma configuração de bloco para capturar a saída padrão, já que a chamada real do system está enterrada no código que está sendo testado:

 require 'tempfile' def capture_stdout stdout = $stdout.dup Tempfile.open 'stdout-redirect' do |temp| $stdout.reopen temp.path, 'w+' yield if block_given? $stdout.reopen stdout temp.read end end 

Isso nos dá um método que captura qualquer saída no bloco dado usando um arquivo temporário para armazenar os dados reais. Exemplo de uso:

 captured_content = capture_stdout do system 'echo foo' end puts captured_content 

É claro que você pode replace a chamada do system por qualquer coisa que possa chamar system internamente. Você também pode usar o mesmo método para capturar stderr se quiser.

Como sistema de substituição direta (…) você pode usar Open3.popen3 (…)

Discussão adicional: http://tech.natemurray.com/2007/03/ruby-shell-commands.html

Eu não encontrei este aqui, então adicionando, eu tive alguns problemas para obter a saída completa.

Você pode redirect STDERR para STDOUT se quiser capturar STDERR usando o backtick.

output = `grep hosts / private / etc / * 2> & 1`

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

 puts `date` puts $? Mon Mar 7 19:01:15 PST 2016 pid 13093 exit 0 
    Intereting Posts