Embora essa pergunta seja útil, ela não é bem feita. O Ruby tem várias maneiras de chamar subconjuntos que são bem documentados e facilmente encontrados lendo a documentação do Kernel e do Open3 e pesquisando aqui no SO.
the Tin Man
1
Infelizmente, este tópico é bastante complexo. Open3( docs ) é a melhor opção para a maioria das situações, IMO, mas em versões mais antigas do Ruby, ele não respeitará um código modificado PATH( bugs.ruby-lang.org/issues/8004 ), e dependendo de como você passa os argumentos (especificamente , se você usar hash op com não-palavras-chave), ele poderá ser quebrado. Mas, se você enfrentar essas situações, estará fazendo algo bem avançado e poderá descobrir o que fazer lendo a implementação de Open3.
Joshua Cheek
3
Estou surpreso que ninguém tenha mencionado Shellwords.escape( doc ). Você não deseja inserir dados do usuário diretamente nos comandos do shell - escape primeiro! Veja também comando de injeção .
Kelvin
Respostas:
1319
Essa explicação é baseada em um script Ruby comentado de um amigo meu. Se você deseja melhorar o script, fique à vontade para atualizá-lo no link.
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 por /bin/shtodos os sistemas.
Aqui estão algumas maneiras de executar um script de shell:
cmd ="echo 'hi'"# Sample string that can be used
Kernel#` , comumente chamado backticks - `cmd`
É como muitas outras linguagens, incluindo Bash, PHP e Perl.
Retorna o resultado (isto é, saída padrão) do comando shell.
Após o xcaractere, há um delimitador, que pode ser qualquer caractere. Se o delimitador é um dos personagens (, [, {, ou <, o literal consiste dos personagens até o delimitador de fechamento correspondente, tendo em conta pares de delimitadores aninhadas. Para todos os outros delimitadores, o literal compreende os caracteres até a próxima ocorrência do caractere delimitador. A interpolação de string #{ ... }é permitida.
Retorna o resultado (ou seja, saída padrão) do comando shell, assim como os backticks.
exec("echo 'hi'")exec( cmd )# Note: this will never be reached because of the line above
Aqui estão alguns conselhos adicionais:,
$?que é o mesmo que $CHILD_STATUS, acessa o status do último comando executado pelo sistema se você usar os backticks, system()ou %x{}. Você pode acessar as propriedades exitstatuse pid:
Preciso registrar as saídas do meu executável no servidor de produção, mas não encontrei nenhuma maneira. Eu usei puts #{cmd}e logger.info ( #{cmd}). Existe alguma maneira de registrar suas saídas na produção?
Por uma questão de completude (como eu pensei que isso também seria um comando Ruby): Rake tem sh, que executa "Execute o comando do sistema cmd. Se forem fornecidos vários argumentos, o comando não será executado com o shell (mesma semântica que o Kernel :: exec e Kernel :: system) ".
sschuberth
40
Backticks não capturam STDERR por padrão. Acrescente `2> & 1` ao comando se você deseja capturar #
Andrei Botalov 17/02/2013
14
Eu acho que essa resposta seria ligeiramente melhorada se dissesse que backticks e% x retornavam a "saída", em vez do "resultado", do comando fornecido. Este último pode ser confundido com status de saída. Ou isso sou só eu?
Uau haha. Muito útil, mesmo que o fato de que isso exista seja infeliz #
Josh Bodah 19/12/16
Como uma observação lateral, acho o método spawn () encontrado em muitos lugares diferentes (por exemplo, Kernele Processque é mais versátil. É mais ou menos o mesmo PTY.spawn(), mas mais genérico.)
Smar
160
A maneira que eu gosto de fazer isso é usar o %xliteral, 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 fileend
o acima não funciona para mim. `` <main> ': método indefinido each' for :String (NoMethodError) como funcionou para você? Estou usando ruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]Você tem certeza de que uma matriz é retornada do comando para que o loop realmente funcione?
Nasser
% x [cmd] .split ("\ n") retornará uma lista :) :)
Eu também gosto do open3, especialmente do Open3.capture3: ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/… -> stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
severin
Existe alguma documentação sobre como executar testes de especificação e unidade com o Open3 ou outros Open's no std-lib do Ruby? É difícil testar shell outs no meu nível atual de entendimento.
FilBot3
29
Algumas coisas a considerar ao escolher entre esses mecanismos são:
Você só quer stdout ou precisa de stderr também? Ou mesmo separados?
Qual é o tamanho da sua saída? Deseja manter o resultado inteiro na memória?
Deseja ler parte de sua saída enquanto o subprocesso ainda está em execução?
Você precisa de códigos de resultado?
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.popenaté completos Kernel.fork/ Kernel.execcom IO.pipee IO.select.
Você também pode colocar limites de tempo na mistura se um subprocesso demorar muito para ser executado.
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 por /bin/shtodos os sistemas.
Se você precisar usar o Bash, insira bash -c "your Bash-only command"dentro do método de chamada desejado:
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.
Usando as respostas aqui e vinculadas na resposta de Mihai, montei uma função que atende a esses requisitos:
Captura perfeitamente STDOUT e STDERR para que não "vazem" quando meu script é executado no console.
Permite que os argumentos sejam passados para o shell como uma matriz, portanto, não há necessidade de se preocupar em escapar.
Captura o status de saída do comando para que fique claro quando ocorrer um erro.
Como um bônus, este também retornará STDOUT nos casos em que o comando shell sair com sucesso (0) e colocar qualquer coisa em STDOUT. Dessa maneira, difere de system, que simplesmente retorna truenesses casos.
Código a seguir. A função específica é system_quietly:
require'open3'classShellError<StandardError;end#actual function:def system_quietly(*cmd)
exit_status=nil
err=nilout=nilOpen3.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.valueendif exit_status.to_i >0
err = err.chomp if err
raiseShellError, err
elsifoutreturnout.chomp
elsereturntrueendend#calling it:begin
puts system_quietly('which','ruby')rescueShellError
abort "Looks like you don't have the `ruby` command. Odd."end#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
Não esqueça o spawncomando para criar um processo em segundo plano para executar o comando especificado. Você pode até esperar sua conclusão usando a Processclasse e a retornada pid:
Kernel.spawn()parece ser muito mais versátil do que todas as outras opções.
Kashyap
6
Se você tiver um caso mais complexo do que o caso comum que não pode ser tratado ``, faça check-out Kernel.spawn(). Este parece ser o mais genérico / com todos os recursos fornecidos pelo estoque Ruby para executar comandos externos.
Você pode usá-lo para:
crie grupos de processos (Windows).
redirecionar dentro, fora, erro para arquivos / uns aos outros.
definir env vars, umask.
altere o diretório antes de executar um comando.
definir limites de recursos para CPU / dados / etc.
Faça tudo o que puder ser feito com outras opções em outras respostas, mas com mais código.
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 =>trueor0: 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.SeeProcess.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,...)ornot:close_others =>false: inherit fds (defaultfor system andexec):close_others =>true: dont inherit (defaultfor spawn and IO.popen)
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 quanto
system("thecommand")
ou
`thecommand`
nos backticks, uma coisa boa desse método, em comparação com outros métodos, é que backticks parecem não me deixar putso comando que eu corro / armazeno o comando que eu quero executar em uma variável e system("thecommand")não me permite obter a saída, enquanto esse método me permite fazer as duas coisas e permite acessar stdin, stdout e stderr independentemente.
Esta não é realmente uma resposta, mas talvez alguém ache útil:
Ao usar a TK GUI no Windows, e você precisa chamar comandos de shell do rubyw, sempre haverá uma janela CMD irritante aparecendo por menos de um segundo.
Aqui está um exemplo legal 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 )
Open3
( docs ) é a melhor opção para a maioria das situações, IMO, mas em versões mais antigas do Ruby, ele não respeitará um código modificadoPATH
( bugs.ruby-lang.org/issues/8004 ), e dependendo de como você passa os argumentos (especificamente , se você usar hash op com não-palavras-chave), ele poderá ser quebrado. Mas, se você enfrentar essas situações, estará fazendo algo bem avançado e poderá descobrir o que fazer lendo a implementação deOpen3
.Shellwords.escape
( doc ). Você não deseja inserir dados do usuário diretamente nos comandos do shell - escape primeiro! Veja também comando de injeção .Respostas:
Essa explicação é baseada em um script Ruby comentado de um amigo meu. Se você deseja melhorar o script, fique à vontade para atualizá-lo no link.
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 por/bin/sh
todos os sistemas.Aqui estão algumas maneiras de executar um script de shell:
Kernel#`
, comumente chamado backticks -`cmd`
É como muitas outras linguagens, incluindo Bash, PHP e Perl.
Retorna o resultado (isto é, saída padrão) do comando shell.
Documentos: http://ruby-doc.org/core/Kernel.html#method-i-60
Sintaxe incorporada,
%x( cmd )
Após o
x
caractere, há um delimitador, que pode ser qualquer caractere. Se o delimitador é um dos personagens(
,[
,{
, ou<
, o literal consiste dos personagens até o delimitador de fechamento correspondente, tendo em conta pares de delimitadores aninhadas. Para todos os outros delimitadores, o literal compreende os caracteres até a próxima ocorrência do caractere delimitador. A interpolação de string#{ ... }
é permitida.Retorna o resultado (ou seja, saída padrão) do comando shell, assim como os backticks.
Documentos: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings
Kernel#system
Executa o comando fornecido em um subshell.
Retorna
true
se o comando foi encontrado e executado com êxito,false
caso contrário.Documentos: http://ruby-doc.org/core/Kernel.html#method-i-system
Kernel#exec
Substitui o processo atual executando o comando externo fornecido.
Retorna nenhum, o processo atual é substituído e nunca continua.
Documentos: http://ruby-doc.org/core/Kernel.html#method-i-exec
Aqui estão alguns conselhos adicionais:,
$?
que é o mesmo que$CHILD_STATUS
, acessa o status do último comando executado pelo sistema se você usar os backticks,system()
ou%x{}
. Você pode acessar as propriedadesexitstatus
epid
:Para mais leitura, consulte:
fonte
#{cmd}
e logger.info (#{cmd}
). Existe alguma maneira de registrar suas saídas na produção?cmd
. Se forem fornecidos vários argumentos, o comando não será executado com o shell (mesma semântica que o Kernel :: exec e Kernel :: system) ".Aqui está um fluxograma baseado em " Quando usar cada método de inicialização de um subprocesso no Ruby ". Veja também, " Engane um aplicativo para pensar que seu padrão é um terminal, não um tubo ".
fonte
Kernel
eProcess
que é mais versátil. É mais ou menos o mesmoPTY.spawn()
, mas mais genérico.)A maneira que eu gosto de fazer isso é usar o
%x
literal, o que facilita (e é legível!) Usar aspas em um comando, da seguinte maneira: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:
fonte
%x[ cmd ]
retorna um array para você?each' for :String (NoMethodError)
como funcionou para você? Estou usandoruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]
Você tem certeza de que uma matriz é retornada do comando para que o loop realmente funcione?Aqui está o melhor artigo na minha opinião sobre a execução de scripts de shell no Ruby: " 6 maneiras de executar comandos de shell no Ruby ".
Se você só precisa obter a saída, use backticks.
Eu precisava de coisas mais avançadas, como STDOUT e STDERR, então usei a gema Open4. Você tem todos os métodos explicados lá.
fonte
%x
opção de sintaxe.spawn
método quando o encontrei.O meu favorito é o Open3
fonte
stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
Algumas coisas a considerar ao escolher entre esses mecanismos são:
Você pode precisar de qualquer coisa, desde simples backticks (``)
system()
eIO.popen
até completosKernel.fork
/Kernel.exec
comIO.pipe
eIO.select
.Você também pode colocar limites de tempo na mistura se um subprocesso demorar muito para ser executado.
Infelizmente, depende muito .
fonte
Mais uma opção:
Quando você:
Você pode usar o redirecionamento de shell:
A
2>&1
sintaxe funciona no Linux , Mac e Windows desde os primeiros dias do MS-DOS.fonte
Definitivamente, não sou especialista em Ruby, mas vou tentar:
Você também deve ser capaz de fazer coisas como:
fonte
As respostas acima já são ótimas, mas eu realmente quero compartilhar o seguinte artigo de resumo: " 6 maneiras de executar comandos do shell em Ruby "
Basicamente, ele nos diz:
Kernel#exec
:system
e$?
:Backticks (`):
IO#popen
:Open3#popen3
- stdlib:Open4#popen4
- uma jóia:fonte
Se você realmente precisa do Bash, de acordo com a nota na "melhor" resposta.
Se você precisar usar o Bash, insira
bash -c "your Bash-only command"
dentro do método de chamada desejado:Testar:
Ou se você estiver executando um arquivo de script existente como
Ruby deve honrar o shebang, mas você sempre pode usar
para garantir que, embora possa haver uma pequena sobrecarga na
/bin/sh
execução/bin/bash
, você provavelmente não notará.fonte
Você também pode usar os operadores de backtick (`), semelhantes ao Perl:
Ú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.
fonte
Podemos alcançá-lo de várias maneiras.
Usando
Kernel#exec
, nada após a execução deste comando:Usando
backticks or %x
Usando
Kernel#system
command, retornatrue
se for bem-sucedido,false
se malsucedido e retornanil
se a execução do comando falhar:fonte
A maneira mais fácil é, por exemplo:
fonte
Usando as respostas aqui e vinculadas na resposta de Mihai, montei uma função que atende a esses requisitos:
Como um bônus, este também retornará STDOUT nos casos em que o comando shell sair com sucesso (0) e colocar qualquer coisa em STDOUT. Dessa maneira, difere de
system
, que simplesmente retornatrue
nesses casos.Código a seguir. A função específica é
system_quietly
:fonte
Não esqueça o
spawn
comando para criar um processo em segundo plano para executar o comando especificado. Você pode até esperar sua conclusão usando aProcess
classe e a retornadapid
:O documento diz: Este método é semelhante,
#system
mas não espera que o comando seja concluído.fonte
Kernel.spawn()
parece ser muito mais versátil do que todas as outras opções.Se você tiver um caso mais complexo do que o caso comum que não pode ser tratado
``
, faça check-outKernel.spawn()
. Este parece ser o mais genérico / com todos os recursos fornecidos pelo estoque Ruby para executar comandos externos.Você pode usá-lo para:
A documentação do Ruby tem bons exemplos:
fonte
O método backticks (`) é o mais fácil de chamar comandos de shell do Ruby. Retorna o resultado do comando shell:
fonte
Dado um comando como
attrib
:Descobri que, embora esse método não seja tão memorável quanto
ou
nos backticks, uma coisa boa desse método, em comparação com outros métodos, é que backticks parecem não me deixar
puts
o comando que eu corro / armazeno o comando que eu quero executar em uma variável esystem("thecommand")
não me permite obter a saída, enquanto esse método me permite fazer as duas coisas e permite acessar stdin, stdout e stderr independentemente.Consulte " Executando comandos em ruby " e a documentação do Ruby Open3 .
fonte
Esta não é realmente uma resposta, mas talvez alguém ache útil:
Ao usar a TK GUI no Windows, e você precisa chamar comandos de shell do rubyw, sempre haverá uma janela CMD irritante aparecendo por menos de um segundo.
Para evitar isso, você pode usar:
ou
Ambos armazenam a
ipconfig
saída dentrolog.txt
, mas nenhuma janela será exibida.Você precisará
require 'win32ole'
dentro do seu script.system()
,exec()
espawn()
todos aparecerão nessa janela irritante ao usar TK e rubyw.fonte
Aqui está um exemplo legal 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):
fonte