85 votes

Comment puis-je obtenir le nom de la commande appelée pour les invites d'utilisation dans Ruby?

J'ai écrit un petit script Ruby a quelques temps que je suis plutôt friand de. Je voudrais améliorer sa robustesse en vérifiant le bon nombre d'arguments:

if ARGV.length != 2 then
  puts "Usage: <command> arg1 arg2"
end

Bien sûr, c'est pseudo-code. De toute façon, en C ou C++ je pourrais utiliser argv[0] pour obtenir le nom de l'utilisateur utilisé pour se rendre à ma commande, si ils l'ont appelé comme ./myScript.rb ou myScript.rb ou /usr/local/bin/myScript.rb. En Ruby, je sais qu' ARGV[0] est le premier vrai argument, et ARGV ne contient pas le nom de la commande. Est-il possible que je peux l'obtenir?

154voto

the Tin Man Points 69148

Ruby a trois façons de nous donner le nom du script:

#!/usr/bin/env ruby

puts "$0            : #{$0}"
puts "__FILE__      : #{__FILE__}"
puts "$PROGRAM_NAME : #{$PROGRAM_NAME}"

Enregistrer le code "test.rb" et de l'appeler un couple de façons montre que le script reçoit le nom tel qu'il a été transmis par le système d'exploitation. Un script seul sait ce que l'OS lui dit:

$ ./test.rb 
$0            : ./test.rb
__FILE__      : ./test.rb
$PROGRAM_NAME : ./test.rb

$ ~/Desktop/test.rb 
$0            : /Users/ttm/Desktop/test.rb
__FILE__      : /Users/ttm/Desktop/test.rb
$PROGRAM_NAME : /Users/ttm/Desktop/test.rb

$ /Users/ttm/Desktop/test.rb 
$0            : /Users/ttm/Desktop/test.rb
__FILE__      : /Users/ttm/Desktop/test.rb
$PROGRAM_NAME : /Users/ttm/Desktop/test.rb

L'appel à l'aide de l' ~ de raccourci pour $HOME dans le deuxième exemple montre l'OS de le remplacer par le chemin d'accès étendu, correspondant à ce qui est dans le troisième exemple. Dans tous les cas, c'est ce que l'OS passée.

La liaison vers le fichier en utilisant à la fois dur et doux liens montre un comportement cohérent. J'ai créé un lien en dur pour test1.rb et un doux lien pour test2.rb:

$ ./test1.rb 
$0            : ./test1.rb
__FILE__      : ./test1.rb
$PROGRAM_NAME : ./test1.rb

$ ./test2.rb 
$0            : ./test2.rb
__FILE__      : ./test2.rb
$PROGRAM_NAME : ./test2.rb

Lancement ruby test.rb avec l'une des variations sur le nom du script renvoie des résultats cohérents.

Si vous voulez seulement l'appelle un fichier, vous pouvez utiliser du Fichier basename méthode avec l'une des variables ou split sur le séparateur et de prendre le dernier élément.

$0 et __FILE__ ont quelques différences mineures, mais pour de simples scripts elles sont équivalentes.

puts File.basename($0)

Petit ajout:

Il y a certains avantages à l'utilisation de l' File.basename,File.extname et File.dirname ensemble de méthodes. basename prend un paramètre optionnel, qui est l'extension de la bande, donc si vous avez besoin simplement le nom de base sans l'extension

File.basename($0, File.extname($0)) 

il le fait sans avoir à réinventer la roue ou d'avoir à traiter avec longueur variable ou extensions manquantes ou la possibilité de mal tronquer l'extension des chaînes ".rb.txt" par exemple:

ruby-1.9.2-p136 :004 > filename = '/path/to/file/name.ext'
 => "/path/to/file/name.ext" 
ruby-1.9.2-p136 :005 > File.basename(filename, File.extname(filename))
 => "name" 
ruby-1.9.2-p136 :006 > filename = '/path/to/file/name.ext' << '.txt'
 => "/path/to/file/name.ext.txt" 
ruby-1.9.2-p136 :007 > File.basename(filename, File.extname(filename))
 => "name.ext" 

18voto

Danyel Points 826

cette réponse pourrait venir un peu en retard, mais j'ai eu le même problème et l'on a accepté la réponse ne semble pas tout à fait satisfaisant pour moi, donc j'ai étudié un peu plus loin.

Ce qui me dérange est le fait que l' $0 ou $PROGRAM_NAME n'ont pas vraiment la bonne information sur ce que l'utilisateur a tapé. Si mon script Ruby était dans un dossier du CHEMIN d'accès et l'utilisateur a entré le nom de l'exécutable (sans chemin définitions ./script ou /bin/script), il serait toujours élargir le chemin d'accès total.

Je pensais que c'était un Rubis deficite, j'ai donc essayé la même chose avec Python et à mon grand regret là, il n'était pas différent.

Un ami m'a suggéré un hack pour chercher l' real thing en /proc/self/cmdline, et le résultat était: [ruby, /home/danyel/bin/myscript, arg1, arg2...] (séparés par le null-char). Le méchant ici, est - execve(1) qui s'étend de la chemin de la chemin totale quand il passe à un interprète.

Exemple de programme C:

#include <stdlib.h>
#include <unistd.h>

extern char** environ;
int main() {
  char ** arr = malloc(10 * sizeof(char*));
  arr[0] = "myscript";
  arr[1] = "-h";
  arr[2] = NULL;
  execve("/home/danyel/bin/myscript", arr, environ);
}

Sortie: `Usage: /home/danyel/bin/script FICHIER...

Pour prouver que c'est effectivement un execve chose et pas de bash, nous pouvons créer un mannequin interprète qui ne fait rien mais imprimer les arguments passés à elle:

// interpreter.c
int main(int argc, const char ** argv) {
  while(*argv)
    printf("%s\n", *(argv++));
}

Nous le compiler et de le placer dans un dossier du chemin d'accès (ou de mettre le chemin complet après le shebang) et de créer un mannequin script en ~/bin/myscript/

#!/usr/bin/env interpreter
Hi there!

Maintenant, dans notre main.c:

#include <stdlib.h>

extern char** environ;
int main() {
  char ** arr = malloc(10 * sizeof(char*));
  arr[0] = "This will be totally ignored by execve.";
  arr[1] = "-v";
  arr[2] = "/var/log/apache2.log";
  arr[3] = NULL;
  execve("/home/danyel/bin/myscript", arr, environ);
}

La compilation et l'exécution ./main: interprète /home/danyel/bin/script -v /var/log/apache2.journal

La raison la plus probable est que si le script est dans votre CHEMIN d'accès et le chemin d'accès complet étaient pas fournis, l'interprète de reconnaître cela comme un No such file d'erreur, ce qu'il fait si vous n': ruby myrubyscript --options arg1 et vous n'êtes pas dans le dossier contenant le script.

3voto

Chris Heald Points 28814

Ce n'est pas tout à fait une réponse à votre question, mais on dirait que vous réinventez une roue. Regardez la bibliothèque optparse . Il vous permet de définir des commutateurs de ligne de commande, des arguments, etc., et fera tout le travail lourd pour vous.

3voto

pierr Points 9148

Utilisez $0 ou $PROGRAM_NAME pour obtenir le nom du fichier en cours d'exécution.

Prograide.com

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.

Powered by:

X