Ruby, Différence entre exec, system et% x () ou Backticks

370

Quelle est la différence entre les méthodes Ruby suivantes?

exec, systemEt %x()ou contre - apostrophes

Je sais qu'ils sont utilisés pour exécuter des commandes de terminal par programmation via Ruby, mais j'aimerais savoir pourquoi il existe trois façons différentes de le faire.

Mr Noir
la source
1
Ces commandes, et bien d' autres, sont expliqués très bien dans les docs: exec système des accents graves
Zetetic
1
Il existe un excellent article Ruby Quicktips sur ce sujet: Exécuter des commandes shell .
Simon Perepelitsa
6
Puisque quelqu'un vient de déterrer ce vieux fil de discussion, "Travailler avec des processus Unix" est un excellent livre pour les rubyistes intéressés par le sujet: workingwithunixprocesses.com
Michael Kohl
1
Je suis surpris qu'aucune des réponses ne mentionne sh.
Dennis
@Dennis Quand je posais cette question, ruby ​​1.9.3 * n'est pas sorti.
M. Black

Réponses:

411

système

La systemméthode appelle un programme système. Vous devez fournir la commande comme argument de chaîne à cette méthode. Par exemple:

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

Le programme appelé utilisera le courant STDIN, STDOUTet des STDERRobjets de votre programme Ruby. En fait, la valeur de retour réelle est soit true, falsesoit nil. Dans l'exemple, la date a été imprimée via l'objet IO de STDIN. La méthode sera renvoyée truesi le processus s'est terminé avec un état zéro, falsesi le processus s'est terminé avec un état différent de zéro et nilsi l'exécution a échoué.

Un autre effet secondaire est que la variable globale $?est définie sur un Process::Statusobjet. Cet objet contiendra des informations sur l'appel lui-même, y compris l'identificateur de processus (PID) du processus appelé et l'état de sortie.

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

Coups de poing

Les backticks (``) appellent un programme système et renvoient sa sortie. Contrairement à la première approche, la commande n'est pas fournie via une chaîne, mais en la plaçant dans une paire de backticks.

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

La variable globale $?est également définie via les backticks. Avec les backticks, vous pouvez également utiliser l'interpolation de chaînes.

%X()

L'utilisation %xest une alternative au style des backticks. Il renverra également la sortie. Comme ses proches %wet %q(entre autres), tout délimiteur suffira tant que les délimiteurs de style support correspondent. Ce moyen %x(date), %x{date}et %x-date-sont tous synonymes. Comme les backticks %xpeuvent utiliser l'interpolation de chaînes.

exec

En utilisant Kernel#execle processus actuel (votre script Ruby) est remplacé par le processus appelé via exec. La méthode peut prendre une chaîne comme argument. Dans ce cas, la chaîne sera soumise à une expansion du shell. Lorsque vous utilisez plusieurs arguments, le premier est utilisé pour exécuter un programme et les éléments suivants sont fournis en tant qu'arguments au programme à appeler.

Open3.popen3

Parfois, les informations requises sont écrites sur une entrée standard ou une erreur standard et vous devez également les contrôler. Voici Open3.popen3utile:

require 'open3'

Open3.popen3("curl http://example.com") do |stdin, stdout, stderr, thread|
   pid = thread.pid
   puts stdout.read.chomp
end
Konrad Reiche
la source
3
Et pour plus de contrôle à grains fins de la façon dont les poignées d'appel STDIN, STDOUT, STDERR, pensez à la Open3.popen3place; par exemple, voir stackoverflow.com/a/10922097/258662
cboettig
1
Merci d'avoir mentionné que les backticks prennent en charge l'interpolation de chaînes, ce qui a résolu mon problème.
ADG
244

Voici un organigramme basé sur cette réponse . Voir aussi, utiliser scriptpour émuler un terminal .

entrez la description de l'image ici

Ian
la source
3
Ce n'est pas si simple. Dans mon cas, "c'était OK (et il fallait) bloquer jusqu'à la fin du processus" pour ensuite utiliser popen3 pour vérifier les sorties STDOUT / STDERR.
Nakilon
Vous pouvez toujours provoquer un appel non bloquant pour bloquer (efficacement) en l'enveloppant dans une boucle while. Vous ne pouvez pas faire un appel bloquant en un appel non bloquant si facilement.
Ian
106

Ils font des choses différentes. execremplace le processus actuel par le nouveau processus et ne revient jamais . systemappelle un autre processus et renvoie sa valeur de sortie au processus en cours. L'utilisation de raccourcis appelle un autre processus et renvoie la sortie de ce processus au processus en cours.

William Pursell
la source