"which in ruby": проверка, существует ли программа в $ PATH из ruby - PullRequest
75 голосов
/ 21 января 2010

мои скрипты сильно зависят от внешних программ и скриптов. Я должен быть уверен, что программа, которую мне нужно вызвать, существует. Вручную, я бы проверил это, используя 'which' в командной строке.

Есть ли эквивалент File.exists? для вещей в $PATH?

(да, я думаю, я мог бы разобрать %x[which scriptINeedToRun], но это не супер элегантно.

Спасибо! Янник


ОБНОВЛЕНИЕ: Вот решение, которое я сохранил:

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

ОБНОВЛЕНИЕ 2: пришло несколько новых ответов - по крайней мере, некоторые из них предлагают лучшие решения.

Обновление 3: гем ptools добавил метод which для класса File.

Ответы [ 16 ]

116 голосов
/ 29 марта 2011

Истинное кроссплатформенное решение, корректно работает в 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

Это не использует сниффинг ОС и учитывает $ PATHEXT, в котором перечислены допустимые расширения файлов для исполняемых файлов в Windows.

Обработка which работает на многих системах, но не на всех.

75 голосов
/ 13 ноября 2013

Используйте find_executable метод из mkmf, который включен в stdlib.

require 'mkmf'

find_executable 'ruby'
#=> "/Users/narkoz/.rvm/rubies/ruby-2.0.0-p0/bin/ruby"

find_executable 'which-ruby'
#=> nil
16 голосов
/ 29 июля 2010
def command?(name)
  `which #{name}`
  $?.success?
end

Изначально взято из hub , который использовал type -t вместо which (но для меня это не удалось как для zsh, так и для bash).

6 голосов
/ 22 октября 2014

Использовать MakeMakefile # find_executable0 с отключенным ведением журнала

Уже есть несколько хороших ответов, но вот что я использую:

require 'mkmf'

def set_mkmf_log(logfile=File::NULL)
  MakeMakefile::Logging.instance_variable_set(:@logfile, logfile)
end

# Return path to cmd as a String, 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

При этом используется недокументированный метод # find_executable0 , вызванный MakeMakefile # find_executable для возврата пути без загромождения стандартного вывода. Метод #which также временно перенаправляет файл журнала mkmf на / dev / null , чтобы предотвратить загромождение текущего рабочего каталога с помощью "mkmf.log" или аналогичного.

5 голосов
/ 21 января 2010

Вы можете получить доступ к системным переменным среды с помощью хэша ENV:

puts ENV['PATH']

Это вернет PATH в вашей системе.Таким образом, если вы хотите узнать, существует ли программа nmap, вы можете сделать это:

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

Это выведет true, если файл найден, или false в противном случае.

3 голосов
/ 01 августа 2010

Вот что я использую. Это не зависит от платформы (File::PATH_SEPARATOR - ":" в Unix и ";" в Windows), ищет только те программные файлы, которые фактически выполняются эффективным пользователем текущего процесса, и завершается, как только программа найдена:

##
# 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
2 голосов
/ 01 марта 2017

У меня есть это:

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

работает как для полных путей, так и для команд:

irb(main):043:0> command?("/bin/bash")
=> "/bin/bash"
irb(main):044:0> command?("bash")
=> "/bin/bash"
irb(main):006:0> command?("bush")
=> nil
2 голосов
/ 29 августа 2014

Это улучшенная версия, основанная на ответе @ mislav . Это позволило бы вводить любые пути и строго следовать тому, как cmd.exe выбирает файл для выполнения в Windows.

# which(cmd) :: string 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 string: #{cmd.inspect}") unless cmd.is_a?(String)
  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
2 голосов
/ 07 августа 2010

Я хотел бы добавить, что which принимает флаг -s для бесшумного режима, который только устанавливает флаг успеха, устраняя необходимость перенаправления вывода.

2 голосов
/ 21 января 2010

Был GEM с именем which_ruby, который был чисто Ruby, реализация которого. Это больше не доступно.

Однако я нашел альтернативную реализацию на чистом Ruby .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...