L'appel de commandes de shell Ruby

voix
862

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

Créé 05/08/2008 à 13:56
source utilisateur
Dans d'autres langues...                            


20 réponses

voix
1k

Cette explication est basée sur un commenté scénario Ruby d'un de mes amis. Si vous voulez améliorer le script, ne hésitez pas à le mettre à jour sur le lien.

Tout d' abord, notez que lorsque Ruby appelle à une coquille, il appelle généralement /bin/sh, pas Bash. Certains syntaxe de Bash ne sont pas pris en charge par /bin/shtous les systèmes.

Voici les façons d'exécuter un script shell:

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

    Ceci est comme beaucoup d'autres langues, 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. Intégré dans la syntaxe, %x( cmd )

    A la suite du xcaractère est un délimiteur, qui peut être tout caractère. Si le délimiteur est l' un des personnages (, [, {ou <, le littéral se compose 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 delimiter. Interpolation de chaîne #{ ... }est autorisée.

    Retourne le résultat de la commande shell, tout comme les accents graves.

    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.

    Retourne truesi la commande a été trouvé et a couru avec succès, falsesinon.

    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 aucune, le processus actuel est remplacé et ne continue.

    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 au statut du dernier système de commande exécuté si vous utilisez les guillemets obliques, system()ou %x{}. Vous pouvez alors accéder aux exitstatuset pidpropriétés:

$?.exitstatus

Pour en savoir plus, voir la lecture:

Créé 05/08/2008 à 15:42
source utilisateur

voix
150

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

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

Ce qui, dans ce cas, peupleront la liste des fichiers avec tous les fichiers de test dans le répertoire en cours, que vous pouvez traiter comme prévu:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
Créé 05/08/2008 à 15:08
source utilisateur

voix
140

Voici un organigramme basé sur cette réponse . Voir aussi, à l' aide scriptd'émuler un terminal .

entrez la description d'image ici

Créé 19/05/2016 à 17:01
source utilisateur

voix
58

Voici le meilleur article à mon avis sur l' exécution des scripts shell dans Ruby: « 6 façons d'exécuter des commandes shell dans Ruby ».

Si vous avez seulement besoin d'obtenir l'utilisation de sortie des accents graves.

Je avais besoin des choses plus avancées comme STDOUT et STDERR donc j'utilisé la pierre précieuse Open4. Vous avez toutes les méthodes expliquées ici.

Créé 02/09/2008 à 12:05
source utilisateur

voix
31

Mon préféré est Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Créé 18/09/2008 à 18:47
source utilisateur

voix
23

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

  1. Voulez-vous simplement stdout ou avez-vous besoin stderr ainsi? ou même séparé?
  2. Quelle est votre sortie? Voulez-vous tenir tout le résultat en mémoire?
  3. Voulez-vous lire une partie de votre sortie alors que le sous-processus est toujours en cours?
  4. Avez-vous besoin des codes de résultat?
  5. Avez-vous besoin d'un objet Ruby qui représente le processus et vous permet de le tuer sur demande?

Vous devrez peut - être quelque chose de simples guillemets obliques ( ``), système (), et IO.popenà part entière Kernel.fork/ Kernel.execavec IO.pipeet IO.select.

Vous pouvez également jeter les délais d'attente dans le mélange si un sous-processus prend trop de temps pour exécuter.

Malheureusement, il est très bien dépend .

Créé 07/08/2008 à 06:10
source utilisateur

voix
19

Une autre option:

Lorsque vous:

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

Vous pouvez utiliser la redirection du shell:

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

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

La 2>&1syntaxe fonctionne sur Linux , Mac et de Windows depuis les premiers jours de MS-DOS.

Créé 16/06/2010 à 03:13
source utilisateur

voix
15

Je ne suis certainement pas un expert Ruby, mais je vais vous donner un coup de feu:

$ irb 
system "echo Hi"
Hi
=> true

Vous devez également être en mesure de faire des choses comme:

cmd = 'ls'
system(cmd)
Créé 05/08/2008 à 14:24
source utilisateur

voix
11

Les réponses ci - dessus sont déjà assez grande, mais je veux vraiment partager l'article résumé suivant: « 6 façons d'exécuter des commandes shell dans Ruby »

En gros, il nous dit:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systemet $?:

system 'false' 
puts $?

( `Contre-apostrophes):

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>]
Créé 07/06/2013 à 03:07
source utilisateur

voix
7

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

Tout d' abord, notez que lorsque Ruby appelle à une coquille, il appelle généralement /bin/sh, pas Bash. Certains syntaxe de Bash ne sont pas pris en charge par /bin/shtous les systèmes.

Si vous devez utiliser Bash, insérer à l' bash -c "your Bash-only command"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 script existant (par exemple script_output = system("./my_script.sh")) Ruby devrait honorer le tralala, mais vous pouvez toujours utiliser system("bash ./my_script.sh")pour vous assurer (bien qu'il puisse y avoir une légère tête de /bin/shcourse /bin/bash, vous ne verrez probablement pas.

Créé 02/06/2017 à 20:14
source utilisateur

voix
7

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 quelque chose de simple.

La méthode que vous voulez utiliser dépend exactement ce que vous essayez d'accomplir; consultez la documentation pour plus de détails sur les différentes méthodes.

Créé 05/08/2008 à 14:57
source utilisateur

voix
5

Ne pas oublier 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 retour 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 la commande à la fin.

Créé 04/11/2015 à 15:04
source utilisateur

voix
5

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

  1. capture Soigneusement stdout et stderr donc ils ne sont pas « fuite » quand mon script est exécuté à partir de la console.
  2. Permet des arguments à passer à la coquille comme un tableau, il n'y a donc pas besoin de se soucier de s'échapper.
  3. Capture l'état de sortie de la commande de sorte qu'il est clair si une erreur est survenue.

En prime, celui - ci sera également de retour STDOUT dans les cas où les sorties de commande shell avec succès (0) et met quoi que ce soit sur la sortie standard. De cette manière, il diffère de systemqui renvoie simplement truedans de tels cas.

Code suit. La fonction spécifique est system_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"
Créé 21/02/2012 à 00:36
source utilisateur

voix
5

Nous pouvons réaliser de multiples façons.

L' utilisation Kernel#exec, rien après cette commande est exécutée:

exec('ls ~')

En utilisant backticks or %x

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

En utilisant la Kernel#systemcommande, retourne en truecas de succès, en falsecas d' échec et retourne nilsi l' exécution de la commande échoue:

system('ls ~')
=> true
Créé 19/02/2012 à 19:07
source utilisateur

voix
4

plus simple est, par exemple:

reboot = `init 6`
puts reboot
Créé 30/03/2017 à 18:13
source utilisateur

voix
3
  • Procédé de backticks `est le plus facile d'appeler les commandes de shell de rubis. Il renvoie le résultat de la commande shell.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
Créé 16/02/2017 à 09:58
source utilisateur

voix
3

Si vous avez un cas plus complexe que dans le cas commun (qui ne peut pas être manipulé avec ``) puis vérifier Kernel.spawn() ici . Cela semble être le plus générique / fonctionnalités complet fourni par actions Ruby pour exécuter des commandes externes.

Par exemple, vous pouvez l'utiliser pour:

  • créer des groupes de processus (Windows)
  • rediriger dedans, dehors, erreur de fichiers / chaque-autre.
  • ensemble vars env, umask
  • dir changer avant exécution de la commande
  • fixer des limites de ressources pour CPU / data / ...
  • Faites tout ce qui peut être fait avec d'autres options dans d'autres réponses, mais avec plus de code.

Officiel documentation rubis a 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)
Créé 11/12/2015 à 14:57
source utilisateur

voix
1

Compte tenu d'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 n'est pas aussi mémorable que le système par exemple (le « thecommand ») ou thecommand à contre-apostrophes, une bonne chose au sujet de cette méthode par rapport à d'autres méthodes .. est par exemple ne semble pas contre-apostrophes me laisser « puts la commande je cours / magasin la commande que je veux courir dans une variable et système ( « thecommand ») ne semble pas me laisser la sortie. Considérant que cette méthode me permet de faire ces deux choses, et il me permet stdin d'accès, stdout et stderr indépendamment.

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

Créé 19/12/2017 à 05:54
source utilisateur

voix
0

Pas vraiment une réponse, mais peut-être quelqu'un trouvera ce utile, et son sujet à ce sujet.

Lorsque vous utilisez l'interface graphique TK sous Windows, et u besoin d'appeler des commandes shell de rubyw, u toujours une fenêtre ennuyeux cmd sauter pour moins d'une seconde.

Pour éviter cela, u peut utiliser

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

ou

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

Les deux mémorisera la sortie de l'intérieur ipconfig « log.txt », mais pas de fenêtres montais.

U devra require 'win32ole'dans votre script.

system(), exec()Et spawn()va tous apparaître cette fenêtre gênant lors de l' utilisation des savoirs traditionnels et rubyw.

Créé 05/07/2018 à 12:55
source utilisateur

voix
-1

Voici un cool que je l'utilise dans un script Ruby sur OS X (pour que je puisse commencer un script et d'obtenir une mise à jour même après avoir désactivé loin de la fenêtre):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Créé 14/10/2014 à 21:12
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more