Bien que cette question soit utile, elle n'est pas bien posée. Ruby a de nombreuses façons d'appeler des sous-shells bien documentés et faciles à trouver en lisant la documentation du noyau et Open3 et en recherchant ici sur SO.
The Tin Man
1
Malheureusement, ce sujet est assez complexe. Open3( docs ) est le meilleur choix pour la plupart des situations, OMI, mais sur les anciennes versions de Ruby, il ne respectera pas une modification PATH( bugs.ruby-lang.org/issues/8004 ), et selon la façon dont vous passez les arguments (en particulier , si vous utilisez le hachage opts avec des non-mots clés), il peut se casser. Mais, si vous rencontrez ces situations, vous faites quelque chose d'assez avancé et vous pouvez savoir quoi faire en lisant l'implémentation de Open3.
Joshua Cheek
3
Je suis surpris que personne n'ait mentionné Shellwords.escape( doc ). Vous ne voulez pas insérer directement les entrées utilisateur dans les commandes shell - échappez-y d'abord! Voir aussi injection de commandes .
Kelvin
Réponses:
1319
Cette explication est basée sur un script Ruby commenté d'un de mes amis. Si vous souhaitez améliorer le script, n'hésitez pas à le mettre à jour sur le lien.
Tout d'abord, notez que lorsque Ruby appelle un shell, il appelle généralement /bin/sh, pas Bash. Certaines syntaxes Bash ne sont pas prises en charge par /bin/shsur tous les systèmes.
Voici comment exécuter un script shell:
cmd ="echo 'hi'"# Sample string that can be used
Kernel#` , communément appelés backticks - `cmd`
C'est comme beaucoup d'autres langages, dont Bash, PHP et Perl.
Renvoie le résultat (c'est-à-dire la sortie standard) de la commande shell.
Le caractère qui suit xest un délimiteur, qui peut être n'importe quel caractère. Si le délimiteur est l' un des personnages (, [, {ou <, le littéral est composé des caractères jusqu'à la fermeture de delimiter correspondant, en tenant compte des paires de délimiteurs imbriquées. Pour tous les autres délimiteurs, le littéral comprend les caractères jusqu'à la prochaine occurrence du caractère délimiteur. L'interpolation de chaînes #{ ... }est autorisée.
Renvoie le résultat (c'est-à-dire la sortie standard) de la commande shell, tout comme les backticks.
exec("echo 'hi'")exec( cmd )# Note: this will never be reached because of the line above
Voici quelques conseils supplémentaires
:, $?qui est le même que $CHILD_STATUS, accède à l'état de la dernière commande exécutée par le système si vous utilisez les raccourcis, system()ou %x{}. Vous pouvez ensuite accéder aux propriétés exitstatuset pid:
Je dois enregistrer les sorties de mon exécutable sur le serveur de production mais je n'ai trouvé aucun moyen. J'ai utilisé des #{cmd}options de vente et logger.info ( #{cmd}). Existe-t-il un moyen de consigner leurs sorties sur la production?
Par souci d'exhaustivité (comme je pensais d'abord que ce serait aussi une commande Ruby): Rake a sh qui fait "Exécuter la commande système cmd. Si plusieurs arguments sont donnés, la commande n'est pas exécutée avec le shell (même sémantique que Kernel :: exec et Kernel :: system) ".
sschuberth
40
Les backticks ne capturent pas STDERR par défaut. Ajoutez «2> & 1» à la commande si vous voulez capturer
Andrei Botalov
14
Je pense que cette réponse serait légèrement améliorée si elle disait que les backticks et% x renvoyaient la "sortie", plutôt que le "résultat", de la commande donnée. Ce dernier pourrait être confondu avec le statut de sortie. Ou est-ce juste moi?
Wow haha. Très utile même si le fait que cela doit exister est regrettable
Josh Bodah
En remarque, je trouve la méthode spawn () trouvée dans de nombreux endroits différents (par exemple Kernelet Processpour être la plus polyvalente. C'est plus ou moins la même chose avec PTY.spawn(), mais plus générique.
Smar
160
La façon dont j'aime le faire est d'utiliser le %xlittéral, ce qui rend facile (et lisible!) L'utilisation de guillemets dans une commande, comme ceci:
directorylist =%x[find .-name '*test.rb'| sort]
Qui, dans ce cas, remplira la liste des fichiers avec tous les fichiers de test sous le répertoire actuel, que vous pouvez traiter comme prévu:
directorylist.each do|filename|
filename.chomp!# work with fileend
ce qui précède ne fonctionne pas pour moi. `` <main> ': méthode non définie each' for :String (NoMethodError) comment cela a-t-il fonctionné pour vous? J'utilise ruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]Êtes-vous sûr qu'un tableau est renvoyé par la commande pour que la boucle fonctionne réellement?
Nasser
% x [cmd] .split ("\ n") renverra une liste cependant :)
Existe-t-il de la documentation sur la façon d'effectuer des tests de spécifications et d'unités avec Open3 ou d'autres Open dans Ruby std-lib? Il est difficile de tester les coques à mon niveau actuel de compréhension.
FilBot3
29
Certaines choses à considérer lors du choix entre ces mécanismes sont:
Voulez-vous simplement stdout ou avez-vous également besoin de stderr? Ou même séparé?
Quelle est la taille de votre sortie? Voulez-vous garder le résultat entier en mémoire?
Voulez-vous lire une partie de votre sortie pendant que le sous-processus est toujours en cours d'exécution?
Avez-vous besoin de codes de résultat?
Avez-vous besoin d'un objet Ruby qui représente le processus et vous permet de le tuer à la demande?
Vous pouvez avoir besoin de n'importe quoi, des simples backticks (``),, system()et IO.popenà part entière Kernel.fork/ Kernel.execavec IO.pipeet IO.select.
Vous voudrez peut-être aussi jeter des délais d'attente dans le mix si un sous-processus prend trop de temps à exécuter.
Si vous avez vraiment besoin de Bash, selon la note de la "meilleure" réponse.
Tout d'abord, notez que lorsque Ruby appelle un shell, il appelle généralement /bin/sh, pas Bash. Certaines syntaxes Bash ne sont pas prises en charge par /bin/shsur tous les systèmes.
Si vous devez utiliser Bash, insérez-le à l' bash -c "your Bash-only command"intérieur de la méthode d'appel souhaitée:
Vous pouvez également utiliser les opérateurs backtick (`), similaires à Perl:
directoryListing =`ls /`
puts directoryListing # prints the contents of the root directory
Pratique si vous avez besoin de quelque chose de simple.
La méthode que vous souhaitez utiliser dépend exactement de ce que vous essayez d'accomplir; consultez la documentation pour plus de détails sur les différentes méthodes.
En utilisant les réponses ici et liées dans la réponse de Mihai, j'ai mis en place une fonction qui répond à ces exigences:
Capture parfaitement STDOUT et STDERR afin qu'ils ne "fuient" pas lorsque mon script est exécuté à partir de la console.
Permet de passer des arguments au shell sous forme de tableau, donc vous n'avez pas à vous soucier de l'échappement.
Capture l'état de sortie de la commande pour qu'il soit clair lorsqu'une erreur s'est produite.
En bonus, celui-ci renverra également STDOUT dans les cas où la commande shell se termine avec succès (0) et met quoi que ce soit sur STDOUT. De cette manière, il diffère de system, qui renvoie simplementtrue dans de tels cas.
Le code suit. La fonction spécifique est 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'oubliez pas la spawncommande pour créer un processus d'arrière-plan pour exécuter la commande spécifiée. Vous pouvez même attendre son achèvement en utilisant la Processclasse et le retourné pid:
Kernel.spawn()semble être beaucoup plus polyvalent que toutes les autres options.
Kashyap
6
Si vous avez un cas plus complexe que le cas commun qui ne peut pas être traité ``, consultez Kernel.spawn() . Cela semble être le plus générique / le plus complet fourni par stock Ruby pour exécuter des commandes externes.
Vous pouvez l'utiliser pour:
créer des groupes de processus (Windows).
rediriger in, out, error vers des fichiers / les uns des autres.
définir env vars, umask.
changer le répertoire avant d'exécuter une commande.
définir des limites de ressources pour CPU / données / etc.
Faites tout ce qui peut être fait avec d'autres options dans d'autres réponses, mais avec plus de code.
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
J'ai trouvé que même si cette méthode n'est pas aussi mémorable que
system("thecommand")
ou
`thecommand`
dans les backticks, une bonne chose à propos de cette méthode par rapport aux autres méthodes est que les backticks ne semblent pas me laisser putsla commande que j'exécute / stocke la commande que je veux exécuter dans une variable, etsystem("thecommand") ne semblent pas me permettre d'obtenir la sortie alors que cette méthode me permet de faire ces deux choses, et elle me permet d'accéder à stdin, stdout et stderr indépendamment.
Ce n'est pas vraiment une réponse mais peut-être que quelqu'un la trouvera utile:
Lorsque vous utilisez l'interface graphique TK sous Windows et que vous devez appeler des commandes shell à partir de rubyw, vous aurez toujours une fenêtre CMD ennuyeuse qui apparaît pendant moins d'une seconde.
Voici un cool que j'utilise dans un script ruby sur OS X (afin que je puisse démarrer un script et obtenir une mise à jour même après avoir quitté la fenêtre):
cmd =%Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Open3
( docs ) est le meilleur choix pour la plupart des situations, OMI, mais sur les anciennes versions de Ruby, il ne respectera pas une modificationPATH
( bugs.ruby-lang.org/issues/8004 ), et selon la façon dont vous passez les arguments (en particulier , si vous utilisez le hachage opts avec des non-mots clés), il peut se casser. Mais, si vous rencontrez ces situations, vous faites quelque chose d'assez avancé et vous pouvez savoir quoi faire en lisant l'implémentation deOpen3
.Shellwords.escape
( doc ). Vous ne voulez pas insérer directement les entrées utilisateur dans les commandes shell - échappez-y d'abord! Voir aussi injection de commandes .Réponses:
Cette explication est basée sur un script Ruby commenté d'un de mes amis. Si vous souhaitez améliorer le script, n'hésitez pas à le mettre à jour sur le lien.
Tout d'abord, notez que lorsque Ruby appelle un shell, il appelle généralement
/bin/sh
, pas Bash. Certaines syntaxes Bash ne sont pas prises en charge par/bin/sh
sur tous les systèmes.Voici comment exécuter un script shell:
Kernel#`
, communément appelés backticks -`cmd`
C'est comme beaucoup d'autres langages, dont Bash, PHP et Perl.
Renvoie le résultat (c'est-à-dire la sortie standard) de la commande shell.
Documents: http://ruby-doc.org/core/Kernel.html#method-i-60
Syntaxe intégrée,
%x( cmd )
Le caractère qui suit
x
est un délimiteur, qui peut être n'importe quel caractère. Si le délimiteur est l' un des personnages(
,[
,{
ou<
, le littéral est composé des caractères jusqu'à la fermeture de delimiter correspondant, en tenant compte des paires de délimiteurs imbriquées. Pour tous les autres délimiteurs, le littéral comprend les caractères jusqu'à la prochaine occurrence du caractère délimiteur. L'interpolation de chaînes#{ ... }
est autorisée.Renvoie le résultat (c'est-à-dire la sortie standard) de la commande shell, tout comme les backticks.
Documents: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings
Kernel#system
Exécute la commande donnée dans un sous-shell.
Renvoie
true
si la commande a été trouvée et exécutée correctement,false
sinon.Documents: http://ruby-doc.org/core/Kernel.html#method-i-system
Kernel#exec
Remplace le processus en cours en exécutant la commande externe donnée.
Renvoie none, le processus en cours est remplacé et ne se poursuit jamais.
Documents: http://ruby-doc.org/core/Kernel.html#method-i-exec
Voici quelques conseils supplémentaires :,
$?
qui est le même que$CHILD_STATUS
, accède à l'état de la dernière commande exécutée par le système si vous utilisez les raccourcis,system()
ou%x{}
. Vous pouvez ensuite accéder aux propriétésexitstatus
etpid
:Pour plus de lecture, voir:
la source
#{cmd}
options de vente et logger.info (#{cmd}
). Existe-t-il un moyen de consigner leurs sorties sur la production?cmd
. Si plusieurs arguments sont donnés, la commande n'est pas exécutée avec le shell (même sémantique que Kernel :: exec et Kernel :: system) ".Voici un organigramme basé sur " Quand utiliser chaque méthode de lancement d'un sous-processus dans Ruby ". Voir aussi, " Tromper une application en pensant que sa sortie standard est un terminal, pas un tuyau ".
la source
Kernel
etProcess
pour être la plus polyvalente. C'est plus ou moins la même chose avecPTY.spawn()
, mais plus générique.La façon dont j'aime le faire est d'utiliser le
%x
littéral, ce qui rend facile (et lisible!) L'utilisation de guillemets dans une commande, comme ceci:Qui, dans ce cas, remplira la liste des fichiers avec tous les fichiers de test sous le répertoire actuel, que vous pouvez traiter comme prévu:
la source
%x[ cmd ]
renvoie un tableau?each' for :String (NoMethodError)
comment cela a-t-il fonctionné pour vous? J'utiliseruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]
Êtes-vous sûr qu'un tableau est renvoyé par la commande pour que la boucle fonctionne réellement?Voici le meilleur article à mon avis sur l'exécution de scripts shell dans Ruby: " 6 façons d'exécuter des commandes shell dans Ruby ".
Si vous avez seulement besoin d'obtenir la sortie, utilisez des astuces.
J'avais besoin de choses plus avancées comme STDOUT et STDERR, j'ai donc utilisé le joyau Open4. Vous avez toutes les méthodes expliquées ici.
la source
%x
option de syntaxe.spawn
méthode lorsque j'ai trouvé cela.Mon préféré est Open3
la source
stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
Certaines choses à considérer lors du choix entre ces mécanismes sont:
Vous pouvez avoir besoin de n'importe quoi, des simples backticks (``),,
system()
etIO.popen
à part entièreKernel.fork
/Kernel.exec
avecIO.pipe
etIO.select
.Vous voudrez peut-être aussi jeter des délais d'attente dans le mix si un sous-processus prend trop de temps à exécuter.
Malheureusement, c'est beaucoup dépend.
la source
Encore une option:
Quand vous:
Vous pouvez utiliser la redirection shell:
La
2>&1
syntaxe fonctionne sur Linux , Mac et Windows depuis les premiers jours de MS-DOS.la source
Je ne suis certainement pas un expert Ruby, mais je vais essayer:
Vous devriez également pouvoir faire des choses comme:
la source
Les réponses ci-dessus sont déjà assez bonnes, mais je veux vraiment partager l'article récapitulatif suivant: " 6 façons d'exécuter des commandes shell dans Ruby "
Fondamentalement, il nous dit:
Kernel#exec
:system
et$?
:Pointes arrière (`):
IO#popen
:Open3#popen3
- stdlib:Open4#popen4
-- une gemme:la source
Si vous avez vraiment besoin de Bash, selon la note de la "meilleure" réponse.
Si vous devez utiliser Bash, insérez-le à l'
bash -c "your Bash-only command"
intérieur de la méthode d'appel souhaitée:Tester:
Ou si vous exécutez un fichier de script existant comme
Ruby devrait honorer le shebang, mais vous pouvez toujours utiliser
pour vous assurer, bien qu'il puisse y avoir un léger surcoût lié à l'
/bin/sh
exécution/bin/bash
, vous ne le remarquerez probablement pas.la source
Vous pouvez également utiliser les opérateurs backtick (`), similaires à Perl:
Pratique si vous avez besoin de quelque chose de simple.
La méthode que vous souhaitez utiliser dépend exactement de ce que vous essayez d'accomplir; consultez la documentation pour plus de détails sur les différentes méthodes.
la source
Nous pouvons y parvenir de plusieurs manières.
En utilisant
Kernel#exec
, rien après l'exécution de cette commande:En utilisant
backticks or %x
En utilisant la
Kernel#system
commande, retourne entrue
cas de succès, enfalse
cas d'échec et retournenil
si l'exécution de la commande échoue:la source
La façon la plus simple est, par exemple:
la source
En utilisant les réponses ici et liées dans la réponse de Mihai, j'ai mis en place une fonction qui répond à ces exigences:
En bonus, celui-ci renverra également STDOUT dans les cas où la commande shell se termine avec succès (0) et met quoi que ce soit sur STDOUT. De cette manière, il diffère de
system
, qui renvoie simplementtrue
dans de tels cas.Le code suit. La fonction spécifique est
system_quietly
:la source
N'oubliez pas la
spawn
commande pour créer un processus d'arrière-plan pour exécuter la commande spécifiée. Vous pouvez même attendre son achèvement en utilisant laProcess
classe et le retournépid
:Le doc dit: Cette méthode est similaire
#system
mais n'attend pas la fin de la commande.la source
Kernel.spawn()
semble être beaucoup plus polyvalent que toutes les autres options.Si vous avez un cas plus complexe que le cas commun qui ne peut pas être traité
``
, consultezKernel.spawn()
. Cela semble être le plus générique / le plus complet fourni par stock Ruby pour exécuter des commandes externes.Vous pouvez l'utiliser pour:
La documentation Ruby contient suffisamment d'exemples:
la source
La méthode backticks (`) est la plus simple pour appeler des commandes shell depuis Ruby. Il renvoie le résultat de la commande shell:
la source
Étant donné une commande comme
attrib
:J'ai trouvé que même si cette méthode n'est pas aussi mémorable que
ou
dans les backticks, une bonne chose à propos de cette méthode par rapport aux autres méthodes est que les backticks ne semblent pas me laisser
puts
la commande que j'exécute / stocke la commande que je veux exécuter dans une variable, etsystem("thecommand")
ne semblent pas me permettre d'obtenir la sortie alors que cette méthode me permet de faire ces deux choses, et elle me permet d'accéder à stdin, stdout et stderr indépendamment.Voir " Exécution de commandes dans ruby " et la documentation Ruby Open3 .
la source
Ce n'est pas vraiment une réponse mais peut-être que quelqu'un la trouvera utile:
Lorsque vous utilisez l'interface graphique TK sous Windows et que vous devez appeler des commandes shell à partir de rubyw, vous aurez toujours une fenêtre CMD ennuyeuse qui apparaît pendant moins d'une seconde.
Pour éviter cela, vous pouvez utiliser:
ou
Les deux stockeront la
ipconfig
sortie à l'intérieurlog.txt
, mais aucune fenêtre ne s'affichera.Vous aurez besoin de l'
require 'win32ole'
intérieur de votre script.system()
,exec()
Etspawn()
va tous apparaître cette fenêtre gênant lors de l' utilisation des savoirs traditionnels et rubyw.la source
Voici un cool que j'utilise dans un script ruby sur OS X (afin que je puisse démarrer un script et obtenir une mise à jour même après avoir quitté la fenêtre):
la source