module TTY::Which

A class responsible for finding an executable in the PATH

Constants

VERSION

Public Class Methods

executable_file?(filename, dir = nil) click to toggle source

Determines if filename is an executable file

@example Basic usage

executable_file?("/usr/bin/less") # => true

@example Executable in directory

executable_file?("less", "/usr/bin") # => true
executable_file?("less", "/usr") # => false

@param [String] filename

the path to file

@param [String] dir

the directory within which to search for filename

@return [Boolean]

@api private

# File lib/tty/which.rb, line 128
def executable_file?(filename, dir = nil)
  path = ::File.join(dir, filename) if dir
  path ||= filename
  ::File.file?(path) && ::File.executable?(path)
end
exist?(cmd, paths: search_paths) click to toggle source

Check if executable exists in the path

@param [String] cmd

the executable to check

@param [Array<String>] paths

paths to check

@return [Boolean]

@api public

# File lib/tty/which.rb, line 63
def exist?(cmd, paths: search_paths)
  !which(cmd, paths: paths).nil?
end
extensions(path_ext = ENV["PATHEXT"]) click to toggle source

All possible file extensions

@example

extensions(".exe;cmd;.bat")
# => [".exe", ".bat"]

@param [String] path_ext

a string of semicolon separated filename extensions

@return [Array<String>]

an array with valid file extensions

@api private

# File lib/tty/which.rb, line 104
def extensions(path_ext = ENV["PATHEXT"])
  return [""] unless path_ext

  path_ext.split(::File::PATH_SEPARATOR).select { |part| part.include?(".") }
end
file_with_exec_ext?(filename) click to toggle source

Check if command itself has executable extension

@param [String] filename

the path to executable file

@example

file_with_exec_ext?("file.bat")
# => true

@return [Boolean]

@api private

# File lib/tty/which.rb, line 147
def file_with_exec_ext?(filename)
  extension = ::File.extname(filename)
  return false if extension.empty?

  extensions.any? { |ext| extension.casecmp(ext).zero? }
end
file_with_path?(cmd) click to toggle source

Check if executable file is part of absolute/relative path

@param [String] cmd

the executable to check

@return [Boolean]

@api private

# File lib/tty/which.rb, line 163
def file_with_path?(cmd)
  ::File.expand_path(cmd) == cmd
end
search_paths(path = ENV["PATH"]) click to toggle source

Find default system paths

@param [String] path

the path to search through

@example

search_paths("/usr/local/bin:/bin")
# => ["/bin"]

@return [Array<String>]

the array of paths to search

@api private

# File lib/tty/which.rb, line 81
def search_paths(path = ENV["PATH"])
  paths = if path && !path.empty?
            path.split(::File::PATH_SEPARATOR)
          else
            %w[/usr/local/bin /usr/ucb /usr/bin /bin]
          end
  paths.select(&Dir.method(:exist?))
end
which(cmd, paths: search_paths) click to toggle source

Find an executable in a platform independent way

@param [String] cmd

the command to search for

@param [Array<String>] paths

the paths to look through

@example

which("ruby")                 # => "/usr/local/bin/ruby"
which("/usr/local/bin/ruby")  # => "/usr/local/bin/ruby"
which("foo")                  # => nil

@example

which("ruby", paths: ["/usr/locale/bin", "/usr/bin", "/bin"])

@return [String, nil]

the absolute path to executable if found, `nil` otherwise

@api public

# File lib/tty/which.rb, line 27
def which(cmd, paths: search_paths)
  if file_with_path?(cmd)
    return cmd if executable_file?(cmd)

    extensions.each do |ext|
      exe = "#{cmd}#{ext}"
      return ::File.absolute_path(exe) if executable_file?(exe)
    end
    return nil
  end

  paths.each do |path|
    if file_with_exec_ext?(cmd)
      exe = ::File.join(path, cmd)
      return ::File.absolute_path(exe) if executable_file?(exe)
    end
    extensions.each do |ext|
      exe = ::File.join(path, "#{cmd}#{ext}")
      return ::File.absolute_path(exe) if executable_file?(exe)
    end
  end
  nil
end

Private Instance Methods

executable_file?(filename, dir = nil) click to toggle source

Determines if filename is an executable file

@example Basic usage

executable_file?("/usr/bin/less") # => true

@example Executable in directory

executable_file?("less", "/usr/bin") # => true
executable_file?("less", "/usr") # => false

@param [String] filename

the path to file

@param [String] dir

the directory within which to search for filename

@return [Boolean]

@api private

# File lib/tty/which.rb, line 128
def executable_file?(filename, dir = nil)
  path = ::File.join(dir, filename) if dir
  path ||= filename
  ::File.file?(path) && ::File.executable?(path)
end
exist?(cmd, paths: search_paths) click to toggle source

Check if executable exists in the path

@param [String] cmd

the executable to check

@param [Array<String>] paths

paths to check

@return [Boolean]

@api public

# File lib/tty/which.rb, line 63
def exist?(cmd, paths: search_paths)
  !which(cmd, paths: paths).nil?
end
extensions(path_ext = ENV["PATHEXT"]) click to toggle source

All possible file extensions

@example

extensions(".exe;cmd;.bat")
# => [".exe", ".bat"]

@param [String] path_ext

a string of semicolon separated filename extensions

@return [Array<String>]

an array with valid file extensions

@api private

# File lib/tty/which.rb, line 104
def extensions(path_ext = ENV["PATHEXT"])
  return [""] unless path_ext

  path_ext.split(::File::PATH_SEPARATOR).select { |part| part.include?(".") }
end
file_with_exec_ext?(filename) click to toggle source

Check if command itself has executable extension

@param [String] filename

the path to executable file

@example

file_with_exec_ext?("file.bat")
# => true

@return [Boolean]

@api private

# File lib/tty/which.rb, line 147
def file_with_exec_ext?(filename)
  extension = ::File.extname(filename)
  return false if extension.empty?

  extensions.any? { |ext| extension.casecmp(ext).zero? }
end
file_with_path?(cmd) click to toggle source

Check if executable file is part of absolute/relative path

@param [String] cmd

the executable to check

@return [Boolean]

@api private

# File lib/tty/which.rb, line 163
def file_with_path?(cmd)
  ::File.expand_path(cmd) == cmd
end
search_paths(path = ENV["PATH"]) click to toggle source

Find default system paths

@param [String] path

the path to search through

@example

search_paths("/usr/local/bin:/bin")
# => ["/bin"]

@return [Array<String>]

the array of paths to search

@api private

# File lib/tty/which.rb, line 81
def search_paths(path = ENV["PATH"])
  paths = if path && !path.empty?
            path.split(::File::PATH_SEPARATOR)
          else
            %w[/usr/local/bin /usr/ucb /usr/bin /bin]
          end
  paths.select(&Dir.method(:exist?))
end
which(cmd, paths: search_paths) click to toggle source

Find an executable in a platform independent way

@param [String] cmd

the command to search for

@param [Array<String>] paths

the paths to look through

@example

which("ruby")                 # => "/usr/local/bin/ruby"
which("/usr/local/bin/ruby")  # => "/usr/local/bin/ruby"
which("foo")                  # => nil

@example

which("ruby", paths: ["/usr/locale/bin", "/usr/bin", "/bin"])

@return [String, nil]

the absolute path to executable if found, `nil` otherwise

@api public

# File lib/tty/which.rb, line 27
def which(cmd, paths: search_paths)
  if file_with_path?(cmd)
    return cmd if executable_file?(cmd)

    extensions.each do |ext|
      exe = "#{cmd}#{ext}"
      return ::File.absolute_path(exe) if executable_file?(exe)
    end
    return nil
  end

  paths.each do |path|
    if file_with_exec_ext?(cmd)
      exe = ::File.join(path, cmd)
      return ::File.absolute_path(exe) if executable_file?(exe)
    end
    extensions.each do |ext|
      exe = ::File.join(path, "#{cmd}#{ext}")
      return ::File.absolute_path(exe) if executable_file?(exe)
    end
  end
  nil
end