tags:

views:

259

answers:

8

Hiya, my scripts rely heavily on external programs and scripts. I need to be sure that a program I need to call exists. Manually, I'd check this using 'which' in the commandline.

Is there an equivalent to File.exists? for things in $PATH?

(yes I guess I could parse %x[which scriptINeedToRun] but that's not super elegant.

Thanks! yannick


UPDATE: Here's the solution I retained:

def commandExists?(command)
        system("which #{command} > /dev/null 2>/dev/null")
        return false if $?.exitstatus == 127
        return true
end
+4  A: 

You can access system environment variables with the ENV hash:

puts ENV['PATH']

It will return the PATH on your system. So if you want to know if program nmap exists, you can do this:

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

This will print true if file was found or false otherwise.

rogeriopvl
you should probably also check that the file is executable by the user: File.exists?(...) and File.executable?(...). +1 in any case.
liwp
Please note that this is not cross platform...
kolrie
What about expanding path? Maybe it is also better to use File.join or Pathname. Also why not use which? It is a very good tool and it does its job.
tig
+1  A: 

There was a GEM called which_rubythat was a pure-Ruby which implementation. It's no longer available.

However, I found this pure-Ruby alternative implementation.

Simone Carletti
+1  A: 
bhups
Could be long, better use system("which " + c) then.
philippe
call `cmdExists?('rm -rf ~')`. Also ruby convention is to name methods like `cmd_exists?`
tig
+1  A: 

This is a tweak of rogeriopvl's answer, making it cross platform:

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
kolrie
+2  A: 
def command?(name)
  `which #{name}`
  $?.success?
end

Initially taken from hub, which used type -t instead of which though (and which failed for both zsh and bash for me).

blueyed
+2  A: 

Here's what I'm using. This is platform neutral (File::PATH_SEPARATOR is ":" on Unix and ";" on Windows), only looks for program files that actually are executable by the effective user of the current process, and terminates as soon as the program is found:

##
# 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
Arto Bendiken
A: 

What is not elegant in unix philosophy? Why go through $PATH if there is a tool which does one thing well?

Also I can't find any man page telling that which could return 127.

Ruby system method returns true on 0 exit status, so you need only first line of your method. Also this method doesn't care about escaping command name: use builtin shellwords if you use 1.8.7 or higher version, or something alike if not.

tig
A: 

I'd like to add that which takes the flag -s for silent mode, which only sets the success flag, removing the need for redirecting the output.

olleolleolle