Si j'appelle une commande en utilisant Noyau#système en Ruby, comment puis-je obtenir sa sortie ?
system("ls")
Si j'appelle une commande en utilisant Noyau#système en Ruby, comment puis-je obtenir sa sortie ?
system("ls")
J'aimerais développer et clarifier réponse du chaos un peu.
Si vous entourez votre commande de points de suspension, vous n'avez pas besoin d'appeler (explicitement) system(). Les backticks exécutent la commande et renvoient la sortie sous forme de chaîne. Vous pouvez alors affecter la valeur à une variable comme suit :
output = `ls`
p output
ou
printf output # escapes newline chars
Que faire si je dois donner une variable dans le cadre de ma commande ? En d'autres termes, comment se traduirait quelque chose comme system("ls " + nom du fichier) si l'on devait utiliser des backticks ?
Vous pouvez effectuer l'évaluation des expressions comme vous le feriez avec des chaînes régulières : ls #{filename}
.
Hrm, makdown a mangé ma mise en page. Il y a des tics arrière devant le "l" de "ls" et après le "}" de "#{filename}".
Sachez que toutes les solutions où vous passez une chaîne de caractères contenant des valeurs fournies par l'utilisateur à l'adresse system
, %x[]
etc. ne sont pas sûrs ! Unsafe signifie en fait : l'utilisateur peut déclencher l'exécution du code dans le contexte et avec toutes les permissions du programme.
Pour autant que je puisse dire, seulement system
y Open3.popen3
fournissent une variante sécurisée/escaping dans Ruby 1.8. En Ruby 1.9 IO::popen
accepte également un tableau.
Il suffit de passer chaque option et argument sous forme de tableau à l'un de ces appels.
Si vous avez besoin non seulement de l'état de sortie mais aussi du résultat, vous voudrez probablement utiliser Open3.popen3
:
require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value
Notez que le formulaire de bloc fermera automatiquement stdin, stdout et stderr - sinon, il faudrait que ce soit fermé explicitement .
Plus d'informations ici : Former des commandes shell ou des appels système sanitaires en Ruby
C'est la seule réponse qui réponde réellement à la question et qui résout le problème sans en introduire de nouveaux (intrants non assainis).
Merci ! C'est le genre de réponse que j'espérais. Une correction : le gets
Les appels doivent passer l'argument nil
car sinon nous n'obtenons que la première ligne de la sortie. Donc, par exemple stdout.gets(nil)
.
Cela ne capture que stdout, et stderr va à la console. Pour obtenir stderr, utilisez : output=`ls no_existing_file 2>&1`; result=$?.success?
Cette réponse est non sécurisé et ne devrait pas être utilisé -- si la commande est autre chose qu'une constante, alors la syntaxe backtick est susceptible de provoquer un bogue, voire une faille de sécurité. (Et même s'il s'agit d'une constante, quelqu'un l'utilisera probablement pour une non-constante plus tard et provoquera un bogue). Voir La réponse de Simon Hürlimann pour une solution correcte.
La façon la plus simple de le faire correctement et en toute sécurité est d'utiliser la fonction Open3.capture2()
, Open3.capture2e()
ou Open3.capture3()
.
Utiliser les backticks de ruby et ses %x
Les alias sont NON SÉCURISÉ EN TOUTES CIRCONSTANCES si elle est utilisée avec des données non fiables. Il est DANGEREUX tout simplement :
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
El system
En revanche, les arguments sont échappés correctement si elle est utilisée correctement :
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
Le problème est qu'il renvoie le code de sortie au lieu de la sortie, et que la capture de cette dernière est compliquée et désordonnée.
La meilleure réponse dans ce fil jusqu'à présent mentionne Open3, mais pas les fonctions qui sont les mieux adaptées à cette tâche. Open3.capture2
, capture2e
y capture3
travailler comme system
mais renvoie deux ou trois arguments :
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
Un autre mentionne IO.popen()
. La syntaxe peut être maladroite dans le sens où elle veut un tableau en entrée, mais elle fonctionne aussi :
out = IO.popen(['echo', untrusted]).read # good
Pour plus de commodité, vous pouvez emballer Open3.capture3()
dans une fonction, par exemple :
#
# 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 eol
rescue
end
end
Ejemplo:
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
Donne les résultats suivants :
nil
nil
false
false
/usr/bin/which <— stdout from system('which', 'which')
true <- p system('which', 'which')
"/usr/bin/which" <- p syscall('which', 'which')
C'est la bonne réponse. C'est aussi la plus informative. La seule chose qui manque est un avertissement concernant la fermeture des std*s. Voir cet autre commentaire : require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Notez que le formulaire de bloc fermera automatiquement stdin, stdout et stderr - sinon, il faudrait que ce soit fermé explicitement .
@PeterH.Boling : Autant que je sache, le capture2
, capture2e
y capture3
ferme également les std*s automatiquement. (En tout cas, je n'ai jamais rencontré le problème de mon côté).
Sans utiliser le formulaire de bloc, il n'y a aucun moyen pour une base de code de savoir quand quelque chose doit être fermé, donc je hautement Je doute qu'ils soient fermés. Vous n'avez probablement jamais rencontré de problème parce que le fait de ne pas les fermer ne causera pas de problèmes dans un processus de courte durée, et si vous redémarrez un processus de longue durée assez souvent, otto n'y apparaîtra pas non plus, à moins que vous n'ouvriez des std*s en boucle. Linux a une limite élevée pour les descripteurs de fichiers, que vous pouvez atteindre, mais tant que vous ne l'aurez pas atteinte, vous ne verrez pas le "bug".
Vous pouvez utiliser system() ou %x[] selon le type de résultat dont vous avez besoin.
system() retournant true si la commande a été trouvée et exécutée avec succès, false sinon.
>> s = system 'uptime'
10:56 up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
=> true
>> s.class
=> TrueClass
>> $?.class
=> Process::Status
%x[ ], quant à lui, enregistre les résultats de la commande sous forme de chaîne de caractères :
>> 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
Th article de blog par Jay Fields explique en détail les différences entre l'utilisation de system, exec et %x[ ] .
Merci pour l'astuce d'utiliser %x[]. Cela vient de résoudre un problème que j'avais où j'utilisais des ticks arrière dans un script ruby sous Mac OS X. Lorsque j'ai exécuté le même script sur une machine Windows avec Cygwin, il a échoué à cause des back ticks, mais a fonctionné avec %x[].
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
1 votes
Vous voudrez peut-être jeter un coup d'œil à ce fil dans comp.lang.ruby
0 votes
C'est un fil très manuel, merci. La classe permettant d'exécuter des commandes et d'obtenir un retour d'information est excellente dans l'exemple de code.
5 votes
Pour les futurs googleurs. Si vous voulez en savoir plus sur les autres appels de commandes système et leurs différences, voir cette réponse SO .