“What in ruby”: Vérifier si le programme existe dans $ PATH depuis ruby

mes scripts reposent largement sur des programmes et des scripts externes. Je dois être sûr qu’un programme que je dois appeler existe. Manuellement, je vérifierais ceci en utilisant ‘what’ dans la ligne de commande.

Y a-t-il un équivalent à File.exists? pour des choses dans $PATH ?

(oui je suppose que je pourrais parsingr %x[which scriptINeedToRun] mais ce n’est pas super élégant.

Merci! yannick


MISE À JOUR: Voici la solution que j’ai retenue:

  def command?(command) system("which #{ command} > /dev/null 2>&1") end 

MISE À JOUR 2: Quelques nouvelles réponses ont été apscopes – certaines au moins offrent de meilleures solutions.

Mise à jour 3: Le gem ptools ajoute une méthode ” what ” à la classe File.

Véritable solution multi-plateforme, fonctionne correctement sous Windows:

 # Cross-platform way of finding an executable in the $PATH. # # which('ruby') #=> /usr/bin/ruby def which(cmd) exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| exts.each { |ext| exe = File.join(path, "#{cmd}#{ext}") return exe if File.executable?(exe) && !File.directory?(exe) } end return nil end 

Cela n’utilise pas le reniflage de l’OS hôte et respecte $ PATHEXT qui répertorie les extensions de fichier valides pour les exécutables sous Windows.

Shelling à which fonctionne sur de nombreux systèmes mais pas tous.

Utilisez la méthode find_executable de mkmf qui est incluse dans stdlib.

 require 'mkmf' find_executable 'ruby' #=> "/Users/narkoz/.rvm/rubies/ruby-2.0.0-p0/bin/ruby" find_executable 'which-ruby' #=> nil 
 def command?(name) `which #{name}` $?.success? end 

Initialement pris à partir de hub , qui utilisait le type -t au lieu de which (et qui a échoué pour zsh et bash pour moi).

Vous pouvez accéder aux variables d’environnement du système avec le hachage ENV:

 puts ENV['PATH'] 

Il renverra le PATH sur votre système. Donc, si vous voulez savoir si le programme nmap existe, vous pouvez le faire:

 ENV['PATH'].split(':').each {|folder| puts File.exists?(folder+'/nmap')} 

Cela affichera true si le fichier a été trouvé ou false sinon.

Utilisez MakeMakefile # find_executable0 avec la journalisation désactivée

Il y a déjà un certain nombre de bonnes réponses, mais voici ce que j’utilise:

 require 'mkmf' def set_mkmf_log(logfile=File::NULL) MakeMakefile::Logging.instance_variable_set(:@logfile, logfile) end # Return path to cmd as a Ssortingng, or nil if not found. def which(cmd) old_mkmf_log = MakeMakefile::Logging.instance_variable_get(:@logfile) set_mkmf_log(nil) path_to_cmd = find_executable0(cmd) set_mkmf_log(old_mkmf_log) path_to_cmd end 

Cela utilise la méthode non documentée # find_executable0 appelée par MakeMakefile # find_executable pour renvoyer le chemin sans encombrer la sortie standard. La méthode #which redirige aussi temporairement le fichier journal mkmf vers / dev / null pour éviter d’encombrer le répertoire de travail actuel avec “mkmf.log” ou similaire.

Voici ce que j’utilise. Ceci est neutre pour la plate-forme ( File::PATH_SEPARATOR est ":" sous Unix et ";" sous Windows), recherche uniquement les fichiers de programme réellement exécutables par l’utilisateur effectif du processus en cours et se termine dès que le programme est trouvé :

 ## # Returns +true+ if the +program+ executable is found in the user's path. def has_program?(program) ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory| File.executable?(File.join(directory, program.to_s)) end end 

Je voudrais append ce which prend le drapeau -s pour le mode silencieux, qui ne définit que le drapeau de succès, ce qui supprime la nécessité de redirect la sortie.

Ceci est une version améliorée basée sur la réponse de @ mislav . Cela permettrait n’importe quel type d’entrée de chemin et suit ssortingctement comment cmd.exe choisit le fichier à exécuter dans Windows.

 # which(cmd) :: ssortingng or nil # # Multi-platform implementation of "which". # It may be used with UNIX-based and DOS-based platforms. # # The argument can not only be a simple command name but also a command path # may it be relative or complete. # def which(cmd) raise ArgumentError.new("Argument not a ssortingng: #{cmd.inspect}") unless cmd.is_a?(Ssortingng) return nil if cmd.empty? case RbConfig::CONFIG['host_os'] when /cygwin/ exts = nil when /dos|mswin|^win|mingw|msys/ pathext = ENV['PATHEXT'] exts = pathext ? pathext.split(';').select{ |e| e[0] == '.' } : ['.com', '.exe', '.bat'] else exts = nil end if cmd[File::SEPARATOR] or (File::ALT_SEPARATOR and cmd[File::ALT_SEPARATOR]) if exts ext = File.extname(cmd) if not ext.empty? and exts.any?{ |e| e.casecmp(ext).zero? } \ and File.file?(cmd) and File.executable?(cmd) return File.absolute_path(cmd) end exts.each do |ext| exe = "#{cmd}#{ext}" return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe) end else return File.absolute_path(cmd) if File.file?(cmd) and File.executable?(cmd) end else paths = ENV['PATH'] paths = paths ? paths.split(File::PATH_SEPARATOR).select{ |e| File.directory?(e) } : [] if exts ext = File.extname(cmd) has_valid_ext = (not ext.empty? and exts.any?{ |e| e.casecmp(ext).zero? }) paths.unshift('.').each do |path| if has_valid_ext exe = File.join(path, "#{cmd}") return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe) end exts.each do |ext| exe = File.join(path, "#{cmd}#{ext}") return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe) end end else paths.each do |path| exe = File.join(path, cmd) return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe) end end end nil end 

J’ai ceci:

 def command?(name) [name, *ENV['PATH'].split(File::PATH_SEPARATOR).map {|p| File.join(p, name)} ].find {|f| File.executable?(f)} end 

fonctionne pour les chemins complets ainsi que les commandes:

 irb(main):043:0> command?("/bin/bash") => "/bin/bash" irb(main):044:0> command?("bash") => "/bin/bash" irb(main):006:0> command?("bush") => nil 

Il y avait un GEM appelé which_ruby qui était une implémentation pure-Ruby. Il n’est plus disponible.

Cependant, j’ai trouvé cette implémentation alternative pure-Ruby .

Sur Linux, j’utilise:

 exists = `which #{command}`.size.>(0) 

Malheureusement, which n’est pas une commande POSIX et se comporte donc différemment sur Mac, BSD, etc. (c.-à-d. Génère une erreur si la commande est introuvable). Peut-être que la solution idéale serait d’utiliser

 `command -v #{command}`.size.>(0) # fails!: ruby can't access built-in functions 

Mais cela échoue parce que ruby ​​ne semble pas capable d’accéder aux fonctions intégrées. Mais command -v serait la manière POSIX de le faire.

Solution basée sur rogeriovl, mais fonction complète avec test d’exécution plutôt que test d’existence.

 def command_exists?(command) ENV['PATH'].split(':').each {|folder| File.executable?(File.join(folder, command))} end 

Ne fonctionnera que pour UNIX (Windows n’utilise pas les deux points comme séparateur)

Ceci est un tweak de la réponse de rogeriopvl, ce qui rend la plate-forme croisée:

 require 'rbconfig' def is_windows? Config::CONFIG["host_os"] =~ /mswin|mingw/ end def exists_in_path?(file) ensortinges = ENV['PATH'].split(is_windows? ? ";" : ":") ensortinges.any? {|f| File.exists?("#{f}/#{file}")} end 

pour jruby, toutes les solutions qui dépendent de mkmf peuvent ne pas fonctionner, car elles ont une extension C.

pour jruby, voici un moyen facile de vérifier si quelque chose est exécutable sur le chemin:

 main » unix_process = java.lang.Runtime.getRuntime().exec("git status") => # main » unix_process.exitValue() => 0 main » 

Si l’exécutable n’est pas là, il générera une erreur d’exécution. Vous pouvez donc le faire dans un bloc try / catch dans votre utilisation actuelle.

 ##################################################### # add methods to see if there's an executable that's executable ##################################################### class File class << self ########################################### # exists and executable ########################################### def cmd_executable?(cmd) !ENV['PATH'].split(':').select { |f| executable?(join(f, cmd[/^[^ \n\r]*/])) }.empty? end end end 

Pas tellement élégant mais ça marche :).

 def cmdExists?(c) system(c + " > /dev/null") return false if $?.exitstatus == 127 true end 

Attention : Ceci n’est PAS recommandé, conseil dangereux !