Question Appel des commandes shell depuis Ruby


Comment puis-je appeler des commandes shell depuis l'intérieur d'un programme Ruby? Comment puis-je obtenir la sortie de ces commandes dans Ruby?


883
2017-08-05 12:56


origine


Réponses:


Cette explication est basée sur un commentaire Script Ruby d'un de mes amis. Si vous voulez 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, ne pas Frapper. Une syntaxe Bash n'est pas supportée par /bin/sh sur tous les systèmes.

Voici les moyens d'exécuter un script shell:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , communément appelé backticks - `cmd`

    C'est comme beaucoup d'autres langages, y compris Bash, PHP et Perl.

    Retourne le résultat de la commande shell.

    Docs: http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Syntaxe intégrée, %x( cmd )

    Suivant le x caractère est un délimiteur, qui peut être n'importe quel caractère. Si le délimiteur est l'un des caractères (, [, {, ou <, le littéral est composé des caractères jusqu'au délimiteur de fermeture correspondant, prendre en compte les couples de délimiteurs imbriqués. Pour tous les autres délimiteurs, le littéral comprend les caractères jusqu'à la prochaine occurrence de la caractère délimiteur. Interpolation de chaîne #{ ... } est autorisée.

    Renvoie le résultat de la commande shell, tout comme les backticks.

    Docs: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    Exécute la commande donnée dans un sous-shell.

    Résultats true si la commande a été trouvée et exécutée avec succès, false autrement.

    Docs: http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. 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.

    Docs: http://ruby-doc.org/core/Kernel.html#method-i-exec

    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 backticks, system() ou %x{}. Vous pouvez ensuite accéder à exitstatus et pid Propriétés:

$?.exitstatus

Pour plus de lecture, voir:


1158
2017-08-05 14:42



La façon dont j'aime faire cela est d'utiliser le %x littéral, ce qui le rend facile (et lisible!) pour utiliser des guillemets dans une commande, comme ceci:

directorylist = %x[find . -name '*test.rb' | sort]

Ce qui, dans ce cas, va peupler la liste des fichiers avec tous les fichiers de test sous le répertoire courant, que vous pouvez traiter comme prévu:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

151
2017-08-05 14:08



Voici un organigramme basé sur cette réponse. Voir également, en utilisant script émuler un terminal.

enter image description here


148
2018-05-19 17:01



Voici le meilleur article à mon avis sur l'exécution de scripts shell dans Ruby: "6 façons d'exécuter les commandes Shell dans Ruby".

Si vous avez seulement besoin d'obtenir la sortie, utilisez des backticks.

J'avais besoin de choses plus avancées comme STDOUT et STDERR, j'ai donc utilisé la gemme Open4. Vous avez toutes les méthodes expliquées ici.


58
2017-09-02 11:05



Mon préféré est Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

31
2017-09-18 17:47



Certaines choses à penser lors du choix entre ces mécanismes sont:

  1. Voulez-vous juste stdout ou faites-vous Besoin de stderr aussi bien? ou même séparé?
  2. Quelle est la taille de votre sortie? Veux-tu tenir le résultat entier en mémoire?
  3. Voulez-vous lire certains de vos sortie alors que le sous-processus est encore fonctionnement?
  4. Avez-vous besoin de codes de résultat?
  5. Avez-vous besoin d'un objet rubis représente le processus et vous permet le tuer à la demande?

Vous pouvez avoir besoin de n'importe quoi à partir de simples backticks (``), system (), et IO.popen à part entière Kernel.fork/Kernel.exec avec IO.pipe et IO.select.

Vous pouvez également lancer des délais d'attente dans le mix si un sous-processus prend trop de temps à s'exécuter.

Malheureusement, c'est beaucoup dépend.


23
2017-08-07 05:10



Une option de plus:

Lorsque vous:

  • besoin de stderr ainsi que stdout
  • ne peut / ne veut pas utiliser Open3 / Open4 (ils lancent des exceptions dans NetBeans sur mon Mac, aucune idée pourquoi)

Vous pouvez utiliser la redirection de shell:

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

le 2>&1 la syntaxe fonctionne à travers Linux, Mac et les fenêtres depuis les premiers jours de MS-DOS.


19
2018-06-16 02:13



Je ne suis certainement pas un expert de Ruby, mais je vais essayer:

$ irb 
system "echo Hi"
Hi
=> true

Vous devriez aussi être capable de faire des choses comme:

cmd = 'ls'
system(cmd)

16
2017-08-05 13:24



Les réponses ci-dessus sont déjà assez bonnes, mais je veux vraiment partager l'article de synthèse suivant: "6 façons d'exécuter les commandes Shell dans Ruby"

Fondamentalement, il nous dit:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system et $?:

system 'false' 
puts $?

Backticks (`):

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 - stdlib:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 -- une gemme:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

12
2018-06-07 02:07



Si vous avez vraiment besoin de Bash, par la note dans la "meilleure" réponse.

Tout d'abord, notez que lorsque Ruby appelle un shell, il appelle généralement /bin/sh, ne pas Frapper. Une syntaxe Bash n'est pas supportée par /bin/sh sur tous les systèmes.

Si vous avez besoin d'utiliser Bash, insérez bash -c "your Bash-only command" à l'intérieur de votre méthode d'appel souhaitée.

quick_output = system("ls -la")

quick_bash = system("bash -c 'ls -la'")

Tester:

system("echo $SHELL") system('bash -c "echo $SHELL"')

Ou si vous exécutez un fichier de script existant (par exemple script_output = system("./my_script.sh")) Ruby devrait honorez le shebang, mais vous pouvez toujours utiliser system("bash ./my_script.sh") pour s'assurer (bien qu'il puisse y avoir un léger surcoût de /bin/sh fonctionnement /bin/bash, vous ne le remarquerez probablement pas.


8
2018-06-02 20:14



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 voulez utiliser dépend exactement de ce que vous essayez d'accomplir; consultez les documents pour plus de détails sur les différentes méthodes.


7
2017-08-05 13:57