Ruby, diferença entre exec, system e% x () ou Backticks

370

Qual é a diferença entre os seguintes métodos Ruby?

exec, systeme %x()ou Backticks

Eu sei que eles são usados ​​para executar comandos de terminal programaticamente via Ruby, mas eu gostaria de saber por que existem três maneiras diferentes de fazer isso.

Senhor preto
fonte
11
Estes comandos, e muitos outros, são explicados muito bem nos docs: exec sistema backticks
Zetetic
11
Há um ótimo artigo sobre Ruby Quicktips sobre esse tópico: Executar comandos do shell .
Simon Perepelitsa
6
Desde alguém apenas desenterrado esta discussão antiga, "Trabalhando com Unix processos" é um excelente livro para Rubistas interessados no tema: workingwithunixprocesses.com
Michael Kohl
11
Estou surpreso com nenhuma das respostas mencionadas sh.
Dennis
@ Dennis Quando eu estava levantando essa questão, o ruby ​​1.9.3 * não foi lançado.
Sr. Black

Respostas:

411

sistema

O systemmétodo chama um programa do sistema. Você deve fornecer o comando como um argumento de seqüência de caracteres para esse método. Por exemplo:

>> system("date")
Wed Sep 4 22:03:44 CEST 2013
=> true

O programa Chamado usará o atual STDIN, STDOUTe STDERRobjetos de seu programa Ruby. De fato, o valor de retorno real é ou true, falseou nil. No exemplo, a data foi impressa através do objeto IO de STDIN. O método retornará truese o processo sair com um status zero, falsese o processo sair com um status diferente de zero e nilse a execução falhar.

Outro efeito colateral é que a variável global $?está definida como um Process::Statusobjeto. Este objeto conterá informações sobre a chamada em si, incluindo o identificador do processo (PID) do processo chamado e o status da saída.

>> system("date")
Wed Sep 4 22:11:02 CEST 2013
=> true
>> $?
=> #<Process::Status: pid 15470 exit 0>

Backticks

Os backticks (``) chamam um programa do sistema e retornam sua saída. Ao contrário da primeira abordagem, o comando não é fornecido por meio de uma string, mas colocando-o dentro de um par de reticulares.

>> `date`
=> Wed Sep 4 22:22:51 CEST 2013   

A variável global também $?é definida pelos backticks. Com backticks, você também pode usar a interpolação de strings.

% x ()

O uso %xé uma alternativa ao estilo dos backticks. Também retornará a saída. Como seus parentes %we %q(entre outros), qualquer delimitador será suficiente enquanto os delimitadores de colchetes corresponderem. Este meio %x(date), %x{date}e %x-date-são todos sinônimos. Como backticks %xpodem fazer uso de interpolação de string

exec

Usando Kernel#execo processo atual (seu script Ruby) é substituído pelo processo chamado exec. O método pode usar uma string como argumento. Nesse caso, a string estará sujeita à expansão do shell. Ao usar mais de um argumento, o primeiro é usado para executar um programa e o seguinte é fornecido como argumento para o programa a ser chamado.

Open3.popen3

Às vezes, as informações necessárias são gravadas na entrada ou erro padrão e você também precisa ter controle sobre elas. Aqui Open3.popen3é útil:

require 'open3'

Open3.popen3("curl http://example.com") do |stdin, stdout, stderr, thread|
   pid = thread.pid
   puts stdout.read.chomp
end
Konrad Reiche
fonte
3
E para mais controle de grão fino de como as alças de chamadas STDIN, STDOUT, STDERR, considere Open3.popen3vez; por exemplo, veja stackoverflow.com/a/10922097/258662
cboettig
11
Obrigado por mencionar que os backticks suportam a interpolação de strings que resolveu meu problema.
adg
244

Aqui está um fluxograma com base nesta resposta . Veja também, usando scriptpara emular um terminal .

insira a descrição da imagem aqui

Ian
fonte
3
Isto não é tão simples. No meu caso, "estava OK (e precisa) bloquear até o processo terminar" para usar o popen3 para verificar as saídas STDOUT / STDERR.
Nakilon
Você sempre pode causar uma chamada sem bloqueio para (efetivamente) bloquear, envolvendo-a em um loop while. Você não pode fazer uma chamada bloqueada em uma chamada sem bloqueio com tanta facilidade.
Ian
106

Eles fazem coisas diferentes. execsubstitui o processo atual pelo novo processo e nunca retorna . systemchama outro processo e retorna seu valor de saída para o processo atual. O uso de backticks chama outro processo e retorna a saída desse processo para o processo atual.

William Pursell
fonte