00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 r"""Find the full path to commands.
00028
00029 which(command, path=None, verbose=0, exts=None)
00030 Return the full path to the first match of the given command on the
00031 path.
00032
00033 whichall(command, path=None, verbose=0, exts=None)
00034 Return a list of full paths to all matches of the given command on
00035 the path.
00036
00037 whichgen(command, path=None, verbose=0, exts=None)
00038 Return a generator which will yield full paths to all matches of the
00039 given command on the path.
00040
00041 By default the PATH environment variable is searched (as well as, on
00042 Windows, the AppPaths key in the registry), but a specific 'path' list
00043 to search may be specified as well. On Windows, the PATHEXT environment
00044 variable is applied as appropriate.
00045
00046 If "verbose" is true then a tuple of the form
00047 (<fullpath>, <matched-where-description>)
00048 is returned for each match. The latter element is a textual description
00049 of where the match was found. For example:
00050 from PATH element 0
00051 from HKLM\SOFTWARE\...\perl.exe
00052 """
00053
00054 _cmdlnUsage = """
00055 Show the full path of commands.
00056
00057 Usage:
00058 which [<options>...] [<command-name>...]
00059
00060 Options:
00061 -h, --help Print this help and exit.
00062 -V, --version Print the version info and exit.
00063
00064 -a, --all Print *all* matching paths.
00065 -v, --verbose Print out how matches were located and
00066 show near misses on stderr.
00067 -q, --quiet Just print out matches. I.e., do not print out
00068 near misses.
00069
00070 -p <altpath>, --path=<altpath>
00071 An alternative path (list of directories) may
00072 be specified for searching.
00073 -e <exts>, --exts=<exts>
00074 Specify a list of extensions to consider instead
00075 of the usual list (';'-separate list, Windows
00076 only).
00077
00078 Show the full path to the program that would be run for each given
00079 command name, if any. Which, like GNU's which, returns the number of
00080 failed arguments, or -1 when no <command-name> was given.
00081
00082 Near misses include duplicates, non-regular files and (on Un*x)
00083 files without executable access.
00084 """
00085
00086 __revision__ = "Id: which.py 1448 2007-02-28 19:13:06Z trentm"
00087 __version_info__ = (1, 1, 3)
00088 __version__ = '.'.join(map(str, __version_info__))
00089 __all__ = ["which", "whichall", "whichgen", "WhichError"]
00090
00091 import os
00092 import sys
00093 import getopt
00094 import stat
00095
00096
00097
00098
00099 class WhichError(Exception):
00100 pass
00101
00102
00103
00104
00105
00106 def _getRegisteredExecutable(exeName):
00107 """Windows allow application paths to be registered in the registry."""
00108 registered = None
00109 if sys.platform.startswith('win'):
00110 if os.path.splitext(exeName)[1].lower() != '.exe':
00111 exeName += '.exe'
00112 import _winreg
00113 try:
00114 key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" +\
00115 exeName
00116 value = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, key)
00117 registered = (value, "from HKLM\\"+key)
00118 except _winreg.error:
00119 pass
00120 if registered and not os.path.exists(registered[0]):
00121 registered = None
00122 return registered
00123
00124 def _samefile(fname1, fname2):
00125 if sys.platform.startswith('win'):
00126 return ( os.path.normpath(os.path.normcase(fname1)) ==\
00127 os.path.normpath(os.path.normcase(fname2)) )
00128 else:
00129 return os.path.samefile(fname1, fname2)
00130
00131 def _cull(potential, matches, verbose=0):
00132 """Cull inappropriate matches. Possible reasons:
00133 - a duplicate of a previous match
00134 - not a disk file
00135 - not executable (non-Windows)
00136 If 'potential' is approved it is returned and added to 'matches'.
00137 Otherwise, None is returned.
00138 """
00139 for match in matches:
00140 if _samefile(potential[0], match[0]):
00141 if verbose:
00142 sys.stderr.write("duplicate: %s (%s)\n" % potential)
00143 return None
00144 else:
00145 if not stat.S_ISREG(os.stat(potential[0]).st_mode):
00146 if verbose:
00147 sys.stderr.write("not a regular file: %s (%s)\n" % potential)
00148 elif sys.platform != "win32" \
00149 and not os.access(potential[0], os.X_OK):
00150 if verbose:
00151 sys.stderr.write("no executable access: %s (%s)\n"\
00152 % potential)
00153 else:
00154 matches.append(potential)
00155 return potential
00156
00157
00158
00159
00160 def whichgen(command, path=None, verbose=0, exts=None):
00161 """Return a generator of full paths to the given command.
00162
00163 "command" is a the name of the executable to search for.
00164 "path" is an optional alternate path list to search. The default it
00165 to use the PATH environment variable.
00166 "verbose", if true, will cause a 2-tuple to be returned for each
00167 match. The second element is a textual description of where the
00168 match was found.
00169 "exts" optionally allows one to specify a list of extensions to use
00170 instead of the standard list for this system. This can
00171 effectively be used as an optimization to, for example, avoid
00172 stat's of "foo.vbs" when searching for "foo" and you know it is
00173 not a VisualBasic script but ".vbs" is on PATHEXT. This option
00174 is only supported on Windows.
00175
00176 This method returns a generator which yields either full paths to
00177 the given command or, if verbose, tuples of the form (<path to
00178 command>, <where path found>).
00179 """
00180 matches = []
00181 if path is None:
00182 usingGivenPath = 0
00183 path = os.environ.get("PATH", "").split(os.pathsep)
00184 if sys.platform.startswith("win"):
00185 path.insert(0, os.curdir)
00186 else:
00187 usingGivenPath = 1
00188
00189
00190 if sys.platform.startswith("win"):
00191 if exts is None:
00192 exts = os.environ.get("PATHEXT", "").split(os.pathsep)
00193
00194
00195 for ext in exts:
00196 if ext.lower() == ".exe":
00197 break
00198 else:
00199 exts = ['.CMD', '.COM', '.EXE', '.BAT']
00200 elif not isinstance(exts, list):
00201 raise TypeError("'exts' argument must be a list or None")
00202 else:
00203 if exts is not None:
00204 raise WhichError("'exts' argument is not supported on "\
00205 "platform '%s'" % sys.platform)
00206 exts = []
00207
00208
00209
00210 if os.sep in command or os.altsep and os.altsep in command:
00211 if os.path.exists(command):
00212 match = _cull((command, "explicit path given"), matches, verbose)
00213 if verbose:
00214 yield match
00215 else:
00216 yield match[0]
00217 else:
00218 for i in range(len(path)):
00219 dirName = path[i]
00220
00221 if sys.platform.startswith("win") and len(dirName) >= 2\
00222 and dirName[0] == '"' and dirName[-1] == '"':
00223 dirName = dirName[1:-1]
00224 for ext in ['']+exts:
00225 absName = os.path.abspath(
00226 os.path.normpath(os.path.join(dirName, command+ext)))
00227 if os.path.isfile(absName):
00228 if usingGivenPath:
00229 fromWhere = "from given path element %d" % i
00230 elif not sys.platform.startswith("win"):
00231 fromWhere = "from PATH element %d" % i
00232 elif i == 0:
00233 fromWhere = "from current directory"
00234 else:
00235 fromWhere = "from PATH element %d" % (i-1)
00236 match = _cull((absName, fromWhere), matches, verbose)
00237 if match:
00238 if verbose:
00239 yield match
00240 else:
00241 yield match[0]
00242 match = _getRegisteredExecutable(command)
00243 if match is not None:
00244 match = _cull(match, matches, verbose)
00245 if match:
00246 if verbose:
00247 yield match
00248 else:
00249 yield match[0]
00250
00251
00252 def which(command, path=None, verbose=0, exts=None):
00253 """Return the full path to the first match of the given command on
00254 the path.
00255
00256 "command" is a the name of the executable to search for.
00257 "path" is an optional alternate path list to search. The default it
00258 to use the PATH environment variable.
00259 "verbose", if true, will cause a 2-tuple to be returned. The second
00260 element is a textual description of where the match was found.
00261 "exts" optionally allows one to specify a list of extensions to use
00262 instead of the standard list for this system. This can
00263 effectively be used as an optimization to, for example, avoid
00264 stat's of "foo.vbs" when searching for "foo" and you know it is
00265 not a VisualBasic script but ".vbs" is on PATHEXT. This option
00266 is only supported on Windows.
00267
00268 If no match is found for the command, a WhichError is raised.
00269 """
00270 try:
00271 match = whichgen(command, path, verbose, exts).next()
00272 except StopIteration:
00273 raise WhichError("Could not find '%s' on the path." % command)
00274 return match
00275
00276
00277 def whichall(command, path=None, verbose=0, exts=None):
00278 """Return a list of full paths to all matches of the given command
00279 on the path.
00280
00281 "command" is a the name of the executable to search for.
00282 "path" is an optional alternate path list to search. The default it
00283 to use the PATH environment variable.
00284 "verbose", if true, will cause a 2-tuple to be returned for each
00285 match. The second element is a textual description of where the
00286 match was found.
00287 "exts" optionally allows one to specify a list of extensions to use
00288 instead of the standard list for this system. This can
00289 effectively be used as an optimization to, for example, avoid
00290 stat's of "foo.vbs" when searching for "foo" and you know it is
00291 not a VisualBasic script but ".vbs" is on PATHEXT. This option
00292 is only supported on Windows.
00293 """
00294 return list( whichgen(command, path, verbose, exts) )
00295
00296
00297
00298
00299
00300 def main(argv):
00301 all = 0
00302 verbose = 0
00303 altpath = None
00304 exts = None
00305 try:
00306 optlist, args = getopt.getopt(argv[1:], 'haVvqp:e:',
00307 ['help', 'all', 'version', 'verbose', 'quiet', 'path=', 'exts='])
00308 except getopt.GetoptError, msg:
00309 sys.stderr.write("which: error: %s. Your invocation was: %s\n"\
00310 % (msg, argv))
00311 sys.stderr.write("Try 'which --help'.\n")
00312 return 1
00313 for opt, optarg in optlist:
00314 if opt in ('-h', '--help'):
00315 print _cmdlnUsage
00316 return 0
00317 elif opt in ('-V', '--version'):
00318 print "which %s" % __version__
00319 return 0
00320 elif opt in ('-a', '--all'):
00321 all = 1
00322 elif opt in ('-v', '--verbose'):
00323 verbose = 1
00324 elif opt in ('-q', '--quiet'):
00325 verbose = 0
00326 elif opt in ('-p', '--path'):
00327 if optarg:
00328 altpath = optarg.split(os.pathsep)
00329 else:
00330 altpath = []
00331 elif opt in ('-e', '--exts'):
00332 if optarg:
00333 exts = optarg.split(os.pathsep)
00334 else:
00335 exts = []
00336
00337 if len(args) == 0:
00338 return -1
00339
00340 failures = 0
00341 for arg in args:
00342
00343 nmatches = 0
00344 for match in whichgen(arg, path=altpath, verbose=verbose, exts=exts):
00345 if verbose:
00346 print "%s (%s)" % match
00347 else:
00348 print match
00349 nmatches += 1
00350 if not all:
00351 break
00352 if not nmatches:
00353 failures += 1
00354 return failures
00355
00356
00357 if __name__ == "__main__":
00358 sys.exit( main(sys.argv) )
00359
00360