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:
e se eu precisar fornecer uma variável como parte do meu comando? Ou seja, no que algo como sistema ("ls" + nome do arquivo) se traduz quando os backticks devem ser usados?
Vijay Dev
47
Você pode fazer a avaliação da expressão, tal como faria com cordas regulares: ls #{filename}.
Craig Walker
36
Esta resposta não é aconselhável: apresenta o novo problema de entrada não autorizada do usuário.
Dogweather
4
@ Dogweather: isso pode ser verdade, mas é diferente de qualquer outro método?
Craig Walker
20
se você quiser capturar o stderr, basta colocar 2> & 1 no final do seu comando. por exemplo, a saída =command 2>&1
micred
243
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 systeme Open3.popen3fornecem a / escapando variante segura no Ruby 1.8. No Ruby 1.9 IO::popentambé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:
Esta é a única resposta que realmente responde à pergunta e resolve o problema sem introduzir novas (entrada não autorizada).
Dogweather
2
Obrigado! Esse é o tipo de resposta que eu esperava. Uma correção: as getschamadas devem passar o argumento nil, caso contrário, apenas obtemos a primeira linha da saída. Então, por exemplo stdout.gets(nil).
Alguém sabe se algo mudou no Ruby 2.0 ou 2.1? Edições ou comentários seria apreciada ;-)
Simon Hürlimann
1
Eu acho que a discussão em torno Open3.popen3está 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.writee seu programa fica preso stdout.gets(nil).
hagello
165
Apenas para o registro, se você deseja os dois (resultado de saída e operação), pode:
Era exatamente isso que eu estava procurando. Obrigado.
JDL
12
Isso captura apenas o stdout e o stderr vai para o console. Para obter o stderr, use: output=`ls no_existing_file 2>&1`; result=$?.success?
peterept
8
Essa resposta é insegura e não deve ser usada - se o comando não for uma constante, é provável que a sintaxe do backtick cause um bug, possivelmente uma vulnerabilidade de segurança. (E mesmo que seja uma constante, provavelmente fará com que alguém a use por uma não-constante posteriormente e cause um erro.) Veja a resposta de Simon Hürlimann para obter uma solução correta.
Greg Price
23
parabéns a Greg Price por entender a necessidade de escapar da entrada do usuário, mas não é correto dizer que esta resposta como escrita é insegura. O método Open3 mencionado é mais complicado e introduz mais dependências, e o argumento de que alguém "o usará depois para não ser constante" é um palhaço. É verdade que você provavelmente não os usaria em um aplicativo Rails, mas, para um script de utilitário de sistema simples, sem possibilidade de entrada não confiável do usuário, os backticks são perfeitamente adequados e ninguém deve se sentir mal por usá-los.
O uso de backticks de rubi e seu %xapelido 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 systemfunçã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, capture2ee capture3funciona 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 eolrescueendend
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:
nilnilfalsefalse/usr/bin/which <— stdout from system('which','which')true<- p system('which','which')"/usr/bin/which"<- p syscall('which','which')
Essa é a resposta correta. É também o mais informativo. A única coisa que falta é um aviso sobre o fechamento dos std * s. Veja este outro comentário : 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 .
Peter H. Boling
@ PeterH.Boling: É melhor que eu saiba capture2, capture2ee capture3também os feche std * s automaticamente. (Pelo menos, eu nunca corri para o problema no meu fim.)
Denis de Bernardy
sem usar a forma de bloco não há nenhuma maneira para uma base de código para saber quando algo deve ser fechada, então eu altamente duvido que eles estão sendo fechados. Você provavelmente nunca encontrou um problema porque não fechá-los não causará problemas em um processo de curta duração, e se você reiniciar um processo de execução com bastante frequência, o otto também não aparecerá lá, a menos que você esteja abrindo std * s em uma volta. O Linux tem um limite alto de descritor de arquivos, que você pode atingir, mas até atingi-lo, não verá o "bug".
Peter H. Boling
2
@ PeterH.Boling: Não, não, consulte o código fonte. As funções são apenas wrappers em torno Open3#popen2, popen2ee popen3com um bloco predefinido: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
Denis de Bernardy
1
@Dennis de Barnardy Talvez você tenha perdido o link para a mesma documentação de classe (embora para Ruby 2.0.0, e um método diferente. Ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/… Do exemplo : `` stdin, stdout, stderr, wait_thr = Open3.popen3 ([env,] cmd ... [, opts]) pid = wait_thr [: pid] # pid do processo iniciado ... stdin.close # stdin , stdout e stderr devem ser fechados explicitamente neste formulário. stdout.close stderr.close `` `Eu estava apenas citando a documentação." # stdin, stdout e stderr devem ser fechados explicitamente neste formulário. "
Peter H. Boling
61
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.170.170.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 [..].
Obrigado pela dica de usar% x []. Ele resolveu um problema que eu tinha, quando utilizava back ticks em um script ruby no Mac OS X. Ao executar o mesmo script em uma máquina Windows com Cygwin, ele falhou por causa dos back ticks, mas trabalhou com% x [].
9117 Henrik Warne
22
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"
Não escreve para stdout ou stderr. Vamos tentar este exemplo ruby -e '%x{ls}'- note, sem saída. (fyi %x{}é equivalente a backticks).
ocodo 13/08/2015
Isso funcionou muito bem. Usar shecoaria a saída para o console (ou seja, STDOUT) e também a retornaria. Isso não acontece.
Joshua Pinter
19
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.
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 systemsaí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 systemchamada real está oculta no código que está sendo testado:
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 systemchamada por qualquer coisa que chame internamente system. Você também pode usar um método semelhante para capturar, stderrse quiser.
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 #.
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:
ou
fonte
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
eOpen3.popen3
fornecem a / escapando variante segura no Ruby 1.8. No Ruby 1.9IO::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
: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
fonte
gets
chamadas devem passar o argumentonil
, caso contrário, apenas obtemos a primeira linha da saída. Então, por exemplostdout.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 é suspensostderr.write
e seu programa fica presostdout.gets(nil)
.Apenas para o registro, se você deseja os dois (resultado de saída e operação), pode:
fonte
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()
ouOpen3.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:A
system
função, por outro lado, escapa argumentos corretamente se usada corretamente :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
ecapture3
funciona comosystem
, mas retorna dois ou três argumentos:Outro menciona
IO.popen()
. A sintaxe pode ser desajeitada no sentido em que deseja uma matriz como entrada, mas também funciona:Por conveniência, você pode agrupar
Open3.capture3()
uma função, por exemplo:Exemplo:
Rende o seguinte:
fonte
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
ecapture3
também os feche std * s automaticamente. (Pelo menos, eu nunca corri para o problema no meu fim.)Open3#popen2
,popen2e
epopen3
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.
% x [..] por outro lado, salva os resultados do comando como uma string:
O post de Jay Fields explica em detalhes as diferenças entre usar system, exec e% x [..].
fonte
Se você precisar escapar dos argumentos, no Ruby 1.9, o IO.popen também aceita uma matriz:
Nas versões anteriores, você pode usar o Open3.popen3 :
Se você também precisa passar o stdin, isso deve funcionar em 1.9 e 1.8:
fonte
Você usa backticks:
fonte
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 é:
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.
fonte
Achei que o seguinte é útil se você precisar do valor de retorno:
Eu queria especificamente listar os pids de todos os processos Java na minha máquina e usei isso:
fonte
Como Simon Hürlimann já explicou , o Open3 é mais seguro do que backticks etc.
Observe que o formulário do bloco fecha automaticamente stdin, stdout e stderr; caso contrário, eles precisam ser fechados explicitamente .
fonte
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:Este método captura qualquer saída no bloco especificado usando um arquivo temporário para armazenar os dados reais. Exemplo de uso:
Você pode substituir a
system
chamada por qualquer coisa que chame internamentesystem
. Você também pode usar um método semelhante para capturar,stderr
se quiser.fonte
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 #.
fonte
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
fonte
Não encontrei este aqui, então, ao adicioná-lo, tive alguns problemas para obter a saída completa.
fonte: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
fonte
fonte