web-dev-qa-db-ja.com

"which in Ruby":プログラムがrubyから$ PATHに存在するかどうかを確認する

私のスクリプトは外部プログラムとスクリプトに大きく依存しています。呼び出す必要があるプログラムが存在することを確認する必要があります。手動で、コマンドラインで「which」を使用してこれを確認します。

File.exists?にあるものに$PATHに相当するものはありますか?

(はい、私は%x[which scriptINeedToRun]を解析できたと思いますが、それは非常にエレガントではありません。

ありがとう!ヤニック


更新:私が保持した解決策は次のとおりです。

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

更新2:新しい回答がいくつかあります-少なくともこれらのいくつかはより良い解決策を提供します。

更新3: ptools gem により、「which」メソッドがFileクラスに追加されます。

75
Yannick Wurm

真のクロスプラットフォームソリューションは、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

これはホストOSスニッフィングを使用せず、Windows上の実行可能ファイルの有効なファイル拡張子をリストする$ PATHEXTを尊重します。

whichへのシェルアウトは多くのシステムで機能しますが、すべてではありません。

116
mislav

使用する - find_executable stdlibに含まれるmkmfのメソッド。

require 'mkmf'

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

find_executable 'which-Ruby'
#=> nil
75
NARKOZ
def command?(name)
  `which #{name}`
  $?.success?
end

最初は hub から取得しましたが、whichの代わりにtype -tを使用しました(zshとbashの両方で失敗しました)。

16
blueyed

ロギングを無効にして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」などで現在の作業ディレクトリが乱雑になるのを防ぎます。

6
Todd A. Jacobs

ENVハッシュを使用してシステム環境変数にアクセスできます。

puts ENV['PATH']

システムのPATHを返します。したがって、プログラムnmapが存在するかどうかを知りたい場合、これを行うことができます。

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

これは、ファイルが見つかった場合はtrueを、そうでない場合はfalseを出力します。

5
rogeriopvl

これが私が使用しているものです。これはプラットフォームに依存しません(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
3
Arto Bendiken

whichがサイレントモードのフラグ-sを取り、成功フラグのみを設定し、出力をリダイレクトする必要がないことを追加したいと思います。

2
olleolleolle

私はこれを持っています:

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
inger

which_RubyというGEMがありましたが、これは純粋なRubyの実装でした。もう利用できません。

しかし、私はこれを見つけました pure-Rubyの代替実装

2
Simone Carletti

これは、 @ mislav's answer に基づく改良版です。これにより、あらゆるタイプのパス入力が可能になり、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
konsolebox

Linuxでは次を使用します。

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

残念ながら、whichはPOSIXコマンドではないため、Mac、BSDなどでは異なる動作をします(コマンドが見つからない場合はエラーをスローします)。たぶん、理想的な解決策は

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

しかし、これはRubyは組み込み関数にアクセスできないようです。しかしcommand -vは、これを行うPOSIXの方法です。

1
bwv549

Rogeriovlに基づくソリューションですが、存在テストではなく実行テストで機能を完了します。

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

UNIXでのみ機能します(Windowsは区切り文字としてコロンを使用しません)

1
lzap
#####################################################
# 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
0
Jeremiah

jrubyの場合、mkmfに依存するソリューションはC拡張があるため動作しない可能性があります。

jrubyの場合、パス上で何かが実行可能かどうかを確認する簡単な方法は次のとおりです。

main » unix_process = Java.lang.Runtime.getRuntime().exec("git status")
=> #<Java::JavaLang::UNIXProcess:0x64fa1a79>
main » unix_process.exitValue()
=> 0
main »

実行可能ファイルがない場合、実行時エラーが発生するため、実際の使用ではtry/catchブロックでこれを実行することをお勧めします。

0

これはrogeriopvlの答えの微調整であり、クロスプラットフォームになります。

require 'rbconfig'

def is_windows?
  Config::CONFIG["Host_os"] =~ /mswin|mingw/
end

def exists_in_path?(file)
  entries = ENV['PATH'].split(is_windows? ? ";" : ":")
  entries.any? {|f| File.exists?("#{f}/#{file}")}
end
0
kolrie