Appel de commandes shell depuis Ruby

Translate

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

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

Cette explication est basée sur un commentaireScript rubisd'un de mes amis. Si vous souhaitez améliorer le script, n'hésitez pas à le mettre à jour via le lien.

Tout d'abord, notez que lorsque Ruby appelle un shell, il appelle généralement/bin/sh, ne pasFrapper. Certaines syntaxes Bash ne sont pas prises en charge par/bin/shsur tous les systèmes.

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

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

    C'est comme beaucoup d'autres langages, notamment 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

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

    Suivant lexcaractè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 comprend les caractères jusqu'au délimiteur de fermeture correspondant, en tenant compte des paires de délimiteurs imbriqués. Pour tous les autres délimiteurs, le littéral comprend les caractères jusqu'à la prochaine occurrence du caractère délimiteur. Interpolation de chaîne#{ ... }est autorisée.

    Renvoie le résultat (c'est-à-dire la sortie standard) de la commande shell, tout comme les backticks.

    Documents: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.

    Retourtruesi la commande a été trouvée et exécutée avec succès,falseautrement.

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

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    Remplace le processus actuel en exécutant la commande externe donnée.

    Renvoie aucun, le processus en cours est remplacé et ne continue jamais.

    Documents: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 au statut de la dernière commande exécutée par le système si vous utilisez les backticks,system()ou%x{}. Vous pouvez alors accéder auexitstatusetpidPropriétés:

$?.exitstatus

Pour plus de lecture, voir:

La source
Ian
Translate

Voici un organigramme basé surcette réponse. Voir également,en utilisantscriptpour émuler un terminal.

enter image description here

La source
Translate

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

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

Ce qui, dans ce cas, remplira la liste de 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 file
end
La source
Translate

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 contre-indications.

J'avais besoin de trucs plus avancés comme STDOUT et STDERR donc j'ai utilisé le gem Open4. Vous avez toutes les méthodes expliquées ici.

La source
Translate

Mon préféré estOuvert3

  require "open3"

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

Voici quelques éléments à prendre en compte lors du choix entre ces mécanismes:

  1. Voulez-vous juste stdout ou avez-vous également besoin de stderr? ou même séparés?
  2. Quelle est votre production? Voulez-vous conserver le résultat entier en mémoire?
  3. Voulez-vous lire une partie de votre sortie pendant que le sous-processus est toujours en cours d'exécution?
  4. Avez-vous besoin de codes de résultat?
  5. Avez-vous besoin d'un objet ruby qui représente le processus et vous permet de le tuer à la demande?

Vous aurez peut-être besoin de n'importe quoi, des simples backticks (``), system () etIO.popenà part entièreKernel.fork/Kernel.execavecIO.pipeetIO.select.

Vous pouvez également ajouter des délais d'attente au mixage si un sous-processus prend trop de temps à s'exécuter.

Malheureusement, ildépend.

La source
Translate

Une autre option:

Quand vous:

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

Vous pouvez utiliser la redirection shell:

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

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

le2>&1la syntaxe fonctionne à traversLinux, Mac etles fenêtresdepuis les débuts de MS-DOS.

La source
Translate

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

$ irb 
system "echo Hi"
Hi
=> true

Vous devriez également pouvoir faire des choses comme:

cmd = 'ls'
system(cmd)
La source
Translate

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

En gros, cela nous dit:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systemet$?:

system 'false' 
puts $?

Accompagnement (`):

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>]
La source
Translate

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 pasFrapper. Certaines syntaxes Bash ne sont pas prises en charge par/bin/shsur tous les systèmes.

Si vous devez utiliser Bash, insérezbash -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 exemplescript_output = system("./my_script.sh")) Rubisdevraithonorez le shebang, mais vous pouvez toujours utilisersystem("bash ./my_script.sh")pour être sûr (bien qu'il puisse y avoir un léger frais/bin/shfonctionnement/bin/bash, vous ne le remarquerez probablement pas.

La source
Translate

Vous pouvez également utiliser les opérateurs de 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.

La source
nkm
Translate

Le moyen le plus simple est, par exemple:

reboot = `init 6`
puts reboot
La source
Translate

En utilisant les réponses ici et liées à la réponse de Mihai, j'ai mis en place une fonction qui répond à ces exigences:

  1. Capture soigneusement STDOUT et STDERR afin qu'ils ne «fuient» pas lorsque mon script est exécuté depuis la console.
  2. Permet aux arguments d'être passés au shell sous forme de tableau, donc il n'y a pas besoin de s'inquiéter de l'échappement.
  3. Capture l'état de sortie de la commande pour qu'il soit clair lorsqu'une erreur s'est produite.

En prime, celui-ci retournera é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 desystem, qui renvoie simplementtruedans ces cas.

Le code suit. La fonction spécifique estsystem_quietly:

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.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.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
La source
Translate

Nous pouvons y parvenir de plusieurs manières.

En utilisantKernel#exec, rien après l'exécution de cette commande:

exec('ls ~')

En utilisantbackticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

En utilisantKernel#systemcommande, retournetrueen cas de succès,falseen cas d'échec et retournenilsi l'exécution de la commande échoue:

system('ls ~')
=> true
La source
Translate

N'oubliez pas lespawncommande 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 leProcessclasse et le retournépid:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

Le doc dit: Cette méthode est similaire à#systemmais il n'attend pas que la commande se termine.

La source
Translate

Si vous avez un cas plus complexe que le cas commun (qui ne peut pas être traité avec``) puis vérifiezKernel.spawn() ici. Cela semble être le plus générique / complet fourni parStock Rubispour exécuter des commandes externes.

Par exemple, vous pouvez l'utiliser pour:

  • créer des groupes de processus (Windows)
  • rediriger l'erreur vers les fichiers / les uns des autres.
  • définir des variables d'environnement, umask
  • changer de répertoire avant d'exécuter la commande
  • définir des limites de ressources pour CPU / données / ...
  • Faites tout ce qui peut être fait avec d'autres options dans d'autres réponses, mais avec plus de code.

Officieldocumentation rubya des exemples assez bons.

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 => true or 0 : 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.  See Process.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, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)
La source
ysk
Translate
  • La méthode backticks `est la plus simple pour appeler des commandes shell depuis ruby. Il renvoie le résultat de la commande shell.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
La source
Translate

Étant donné une commande, par exemple attrib

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

J'ai trouvé que bien que cette méthode ne soit pas aussi mémorable que par exemple le système ("la commande") ou la commande dans les backticks, une bonne chose à propos de cette méthode par rapport aux autres méthodes .. c'est par exemple que les backticks ne semblent pas me laisser 'la commande que j'exécute / stocke la commande que je veux exécuter dans une variable, et le système ("la commande") ne semble pas me permettre d'obtenir la sortie. Alors que cette méthode me permet de faire ces deux choses et qu'elle me permet d'accéder indépendamment à stdin, stdout et stderr.

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html

La source
Translate

Ce n'est pas vraiment une réponse, mais peut-être que quelqu'un trouvera cela utile, et cela concerne cela.

Lorsque vous utilisez TK GUI sur Windows, et que vous devez appeler des commandes shell à partir de rubyw, vous aurez toujours une fenêtre cmd ennuyeuse qui apparaîtra pendant moins d'une seconde.

Pour éviter cela, vous pouvez utiliser

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

ou

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

Les deux stockeront la sortie d'ipconfig dans 'log.txt', mais aucune fenêtre ne s'affichera.

Vous devrezrequire 'win32ole'à l'intérieur de votre script.

system(), exec()etspawn()apparaîtra tous cette fenêtre ennuyeuse lors de l'utilisation de TK et rubyw.

La source
Translate

En voici un cool que j'utilise dans un script ruby sur OS X (pour 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 )
La source