Se eu chamar um comando usando o sistema Kernel # no Ruby, como obtenho sua saída?
system("ls")
Se eu chamar um comando usando o sistema Kernel # no Ruby, como obtenho sua saída?
system("ls")
Respostas:
Eu gostaria de expandir e esclarecer um pouco a resposta do caos .
Se você cercar seu comando com reticências, não precisará (explicitamente) chamar system (). Os backticks executam o comando e retornam a saída como uma string. Você pode atribuir o valor a uma variável da seguinte maneira:
output = `ls`
p output
ou
printf output # escapes newline chars
ls #{filename}
.
command 2>&1
Esteja ciente de que todas as soluções em que você passa uma string contendo os valores do usuário fornecido para system
, %x[]
etc. não são seguras! Inseguro significa: o usuário pode acionar o código para ser executado no contexto e com todas as permissões do programa.
Tanto quanto eu posso dizer única system
e Open3.popen3
fornecem a / escapando variante segura no Ruby 1.8. No Ruby 1.9 IO::popen
também aceita uma matriz.
Basta passar todas as opções e argumentos como uma matriz para uma dessas chamadas.
Se você precisar não apenas do status de saída, mas também do resultado que provavelmente deseja usar 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 do bloco fecha automaticamente stdin, stdout e stderr; caso contrário, eles precisam ser fechados explicitamente .
Mais informações aqui: Formação de comandos shell sanitários ou chamadas de sistema em Ruby
gets
chamadas devem passar o argumento nil
, caso contrário, apenas obtemos a primeira linha da saída. Então, por exemplo stdout.gets(nil)
.
Open3.popen3
está perdendo um grande problema: se você tem um subprocesso que grava mais dados no stdout do que um pipe pode suportar, o subprocesso é suspenso stderr.write
e seu programa fica preso stdout.gets(nil)
.
Apenas para o registro, se você deseja os dois (resultado de saída e operação), pode:
output=`ls no_existing_file` ; result=$?.success?
output=`ls no_existing_file 2>&1`; result=$?.success?
A maneira simples de fazer isso corretamente e de forma segura é usar Open3.capture2()
, Open3.capture2e()
ou Open3.capture3()
.
O uso de backticks de rubi e seu %x
apelido NÃO SÃO SEGUROS SOB QUALQUER CIRCUNSTÂNCIA se usados com dados não confiáveis. É PERIGOSO , puro e simples:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
A system
função, por outro lado, escapa 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 em vez da saída e a captura do último é complicada e confusa.
A melhor resposta neste tópico até agora menciona o Open3, mas não as funções mais adequadas para a tarefa. Open3.capture2
, capture2e
e capture3
funciona 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 sintaxe pode ser desajeitada no sentido em que deseja uma matriz como entrada, mas também funciona:
out = IO.popen(['echo', untrusted]).read # good
Por conveniência, você pode agrupar Open3.capture3()
uma função, 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')
Rende 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')
require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Observe que o formulário do bloco fecha automaticamente stdin, stdout e stderr; caso contrário, eles precisam ser fechados explicitamente .
capture2
, capture2e
e capture3
também os feche std * s automaticamente. (Pelo menos, eu nunca corri para o problema no meu fim.)
Open3#popen2
, popen2e
e popen3
com um bloco predefinido: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
Você pode usar system () ou% x [] dependendo do tipo de resultado necessário.
system () retornando true se o comando foi encontrado e executado com êxito; 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
O post de Jay Fields explica em detalhes as diferenças entre usar system, exec e% x [..].
Se você precisar escapar dos argumentos, no Ruby 1.9, o IO.popen também aceita uma matriz:
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 o 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"
Você usa backticks:
`ls`
ruby -e '%x{ls}'
- note, sem saída. (fyi %x{}
é equivalente a backticks).
sh
ecoaria a saída para o console (ou seja, STDOUT) e também a retornaria. Isso não acontece.
Outra maneira é:
f = open("|ls")
foo = f.read()
Observe que esse é o caractere "pipe" antes de "ls" em aberto. Isso também pode ser usado para alimentar dados na entrada padrão do programa e também para ler sua saída padrão.
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 isso:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
Como Simon Hürlimann já explicou , o Open3 é mais seguro do que backticks etc.
require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Observe que o formulário do bloco fecha automaticamente stdin, stdout e stderr; caso contrário, eles precisam ser fechados explicitamente .
Embora o uso de backticks ou popen geralmente seja o que você realmente deseja, ele não responde à pergunta. Pode haver razões válidas para capturar a system
saída (talvez para testes automatizados). Um pouco de pesquisador no Google encontrou uma resposta que pensei em publicar aqui para o benefício de outras pessoas.
Como eu precisava disso para testar, meu exemplo usa uma configuração de bloco para capturar a saída padrão, pois a system
chamada real está oculta 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
Este método captura qualquer saída no bloco especificado 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
Você pode substituir a system
chamada por qualquer coisa que chame internamente system
. Você também pode usar um método semelhante para capturar, stderr
se quiser.
Se você deseja que a saída seja redirecionada para um arquivo usando Kernel#system
, você pode modificar descritores como este:
redirecione stdout e stderr para um arquivo (/ tmp / log) no modo de acréscimo:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Para um comando de longa execução, isso armazenará a saída em tempo real. Você também pode armazenar a saída usando um IO.pipe e redirecioná-la do sistema Kernel #.
Como substituição direta do sistema, você pode usar o Open3.popen3 (...)
Discussões adicionais: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
Não encontrei este aqui, então, ao adicioná-lo, tive alguns problemas para obter a saída completa.
Você pode redirecionar STDERR para STDOUT se desejar capturar o 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