|
Packit Service |
76cb02 |
#!/usr/bin/env python
|
|
Packit Service |
76cb02 |
# Copyright (c) 2002-2005 ActiveState Corp.
|
|
Packit Service |
76cb02 |
# See LICENSE.txt for license details.
|
|
Packit Service |
76cb02 |
# Author:
|
|
Packit Service |
76cb02 |
# Trent Mick (TrentM@ActiveState.com)
|
|
Packit Service |
76cb02 |
# Home:
|
|
Packit Service |
76cb02 |
# http://trentm.com/projects/which/
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
r"""Find the full path to commands.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
which(command, path=None, verbose=0, exts=None)
|
|
Packit Service |
76cb02 |
Return the full path to the first match of the given command on the
|
|
Packit Service |
76cb02 |
path.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
whichall(command, path=None, verbose=0, exts=None)
|
|
Packit Service |
76cb02 |
Return a list of full paths to all matches of the given command on
|
|
Packit Service |
76cb02 |
the path.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
whichgen(command, path=None, verbose=0, exts=None)
|
|
Packit Service |
76cb02 |
Return a generator which will yield full paths to all matches of the
|
|
Packit Service |
76cb02 |
given command on the path.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
By default the PATH environment variable is searched (as well as, on
|
|
Packit Service |
76cb02 |
Windows, the AppPaths key in the registry), but a specific 'path' list
|
|
Packit Service |
76cb02 |
to search may be specified as well. On Windows, the PATHEXT environment
|
|
Packit Service |
76cb02 |
variable is applied as appropriate.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
If "verbose" is true then a tuple of the form
|
|
Packit Service |
76cb02 |
(<fullpath>, <matched-where-description>)
|
|
Packit Service |
76cb02 |
is returned for each match. The latter element is a textual description
|
|
Packit Service |
76cb02 |
of where the match was found. For example:
|
|
Packit Service |
76cb02 |
from PATH element 0
|
|
Packit Service |
76cb02 |
from HKLM\SOFTWARE\...\perl.exe
|
|
Packit Service |
76cb02 |
"""
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
_cmdlnUsage = """
|
|
Packit Service |
76cb02 |
Show the full path of commands.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
Usage:
|
|
Packit Service |
76cb02 |
which [<options>...] [<command-name>...]
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
Options:
|
|
Packit Service |
76cb02 |
-h, --help Print this help and exit.
|
|
Packit Service |
76cb02 |
-V, --version Print the version info and exit.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
-a, --all Print *all* matching paths.
|
|
Packit Service |
76cb02 |
-v, --verbose Print out how matches were located and
|
|
Packit Service |
76cb02 |
show near misses on stderr.
|
|
Packit Service |
76cb02 |
-q, --quiet Just print out matches. I.e., do not print out
|
|
Packit Service |
76cb02 |
near misses.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
-p <altpath>, --path=<altpath>
|
|
Packit Service |
76cb02 |
An alternative path (list of directories) may
|
|
Packit Service |
76cb02 |
be specified for searching.
|
|
Packit Service |
76cb02 |
-e <exts>, --exts=<exts>
|
|
Packit Service |
76cb02 |
Specify a list of extensions to consider instead
|
|
Packit Service |
76cb02 |
of the usual list (';'-separate list, Windows
|
|
Packit Service |
76cb02 |
only).
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
Show the full path to the program that would be run for each given
|
|
Packit Service |
76cb02 |
command name, if any. Which, like GNU's which, returns the number of
|
|
Packit Service |
76cb02 |
failed arguments, or -1 when no <command-name> was given.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
Near misses include duplicates, non-regular files and (on Un*x)
|
|
Packit Service |
76cb02 |
files without executable access.
|
|
Packit Service |
76cb02 |
"""
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
__revision__ = "$Id$"
|
|
Packit Service |
76cb02 |
__version_info__ = (1, 1, 0)
|
|
Packit Service |
76cb02 |
__version__ = '.'.join(map(str, __version_info__))
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
import os
|
|
Packit Service |
76cb02 |
import sys
|
|
Packit Service |
76cb02 |
import getopt
|
|
Packit Service |
76cb02 |
import stat
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
#---- exceptions
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
class WhichError(Exception):
|
|
Packit Service |
76cb02 |
pass
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
#---- internal support stuff
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def _getRegisteredExecutable(exeName):
|
|
Packit Service |
76cb02 |
"""Windows allow application paths to be registered in the registry."""
|
|
Packit Service |
76cb02 |
registered = None
|
|
Packit Service |
76cb02 |
if sys.platform.startswith('win'):
|
|
Packit Service |
76cb02 |
if os.path.splitext(exeName)[1].lower() != '.exe':
|
|
Packit Service |
76cb02 |
exeName += '.exe'
|
|
Packit Service |
76cb02 |
import _winreg
|
|
Packit Service |
76cb02 |
try:
|
|
Packit Service |
76cb02 |
key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" +\
|
|
Packit Service |
76cb02 |
exeName
|
|
Packit Service |
76cb02 |
value = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, key)
|
|
Packit Service |
76cb02 |
registered = (value, "from HKLM\\"+key)
|
|
Packit Service |
76cb02 |
except _winreg.error:
|
|
Packit Service |
76cb02 |
pass
|
|
Packit Service |
76cb02 |
if registered and not os.path.exists(registered[0]):
|
|
Packit Service |
76cb02 |
registered = None
|
|
Packit Service |
76cb02 |
return registered
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def _samefile(fname1, fname2):
|
|
Packit Service |
76cb02 |
if sys.platform.startswith('win'):
|
|
Packit Service |
76cb02 |
return ( os.path.normpath(os.path.normcase(fname1)) ==\
|
|
Packit Service |
76cb02 |
os.path.normpath(os.path.normcase(fname2)) )
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
return os.path.samefile(fname1, fname2)
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def _cull(potential, matches, verbose=0):
|
|
Packit Service |
76cb02 |
"""Cull inappropriate matches. Possible reasons:
|
|
Packit Service |
76cb02 |
- a duplicate of a previous match
|
|
Packit Service |
76cb02 |
- not a disk file
|
|
Packit Service |
76cb02 |
- not executable (non-Windows)
|
|
Packit Service |
76cb02 |
If 'potential' is approved it is returned and added to 'matches'.
|
|
Packit Service |
76cb02 |
Otherwise, None is returned.
|
|
Packit Service |
76cb02 |
"""
|
|
Packit Service |
76cb02 |
for match in matches: # don't yield duplicates
|
|
Packit Service |
76cb02 |
if _samefile(potential[0], match[0]):
|
|
Packit Service |
76cb02 |
if verbose:
|
|
Packit Service |
76cb02 |
sys.stderr.write("duplicate: %s (%s)\n" % potential)
|
|
Packit Service |
76cb02 |
return None
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
if not stat.S_ISREG(os.stat(potential[0]).st_mode):
|
|
Packit Service |
76cb02 |
if verbose:
|
|
Packit Service |
76cb02 |
sys.stderr.write("not a regular file: %s (%s)\n" % potential)
|
|
Packit Service |
76cb02 |
elif not os.access(potential[0], os.X_OK):
|
|
Packit Service |
76cb02 |
if verbose:
|
|
Packit Service |
76cb02 |
sys.stderr.write("no executable access: %s (%s)\n"\
|
|
Packit Service |
76cb02 |
% potential)
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
matches.append(potential)
|
|
Packit Service |
76cb02 |
return potential
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
#---- module API
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def whichgen(command, path=None, verbose=0, exts=None):
|
|
Packit Service |
76cb02 |
"""Return a generator of full paths to the given command.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
"command" is a the name of the executable to search for.
|
|
Packit Service |
76cb02 |
"path" is an optional alternate path list to search. The default it
|
|
Packit Service |
76cb02 |
to use the PATH environment variable.
|
|
Packit Service |
76cb02 |
"verbose", if true, will cause a 2-tuple to be returned for each
|
|
Packit Service |
76cb02 |
match. The second element is a textual description of where the
|
|
Packit Service |
76cb02 |
match was found.
|
|
Packit Service |
76cb02 |
"exts" optionally allows one to specify a list of extensions to use
|
|
Packit Service |
76cb02 |
instead of the standard list for this system. This can
|
|
Packit Service |
76cb02 |
effectively be used as an optimization to, for example, avoid
|
|
Packit Service |
76cb02 |
stat's of "foo.vbs" when searching for "foo" and you know it is
|
|
Packit Service |
76cb02 |
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
|
Packit Service |
76cb02 |
is only supported on Windows.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
This method returns a generator which yields either full paths to
|
|
Packit Service |
76cb02 |
the given command or, if verbose, tuples of the form (
|
|
Packit Service |
76cb02 |
command>, <where path found>).
|
|
Packit Service |
76cb02 |
"""
|
|
Packit Service |
76cb02 |
matches = []
|
|
Packit Service |
76cb02 |
if path is None:
|
|
Packit Service |
76cb02 |
usingGivenPath = 0
|
|
Packit Service |
76cb02 |
path = os.environ.get("PATH", "").split(os.pathsep)
|
|
Packit Service |
76cb02 |
if sys.platform.startswith("win"):
|
|
Packit Service |
76cb02 |
path.insert(0, os.curdir) # implied by Windows shell
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
usingGivenPath = 1
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
# Windows has the concept of a list of extensions (PATHEXT env var).
|
|
Packit Service |
76cb02 |
if sys.platform.startswith("win"):
|
|
Packit Service |
76cb02 |
if exts is None:
|
|
Packit Service |
76cb02 |
exts = os.environ.get("PATHEXT", "").split(os.pathsep)
|
|
Packit Service |
76cb02 |
# If '.exe' is not in exts then obviously this is Win9x and
|
|
Packit Service |
76cb02 |
# or a bogus PATHEXT, then use a reasonable default.
|
|
Packit Service |
76cb02 |
for ext in exts:
|
|
Packit Service |
76cb02 |
if ext.lower() == ".exe":
|
|
Packit Service |
76cb02 |
break
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
exts = ['.COM', '.EXE', '.BAT']
|
|
Packit Service |
76cb02 |
elif not isinstance(exts, list):
|
|
Packit Service |
76cb02 |
raise TypeError("'exts' argument must be a list or None")
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
if exts is not None:
|
|
Packit Service |
76cb02 |
raise WhichError("'exts' argument is not supported on "\
|
|
Packit Service |
76cb02 |
"platform '%s'" % sys.platform)
|
|
Packit Service |
76cb02 |
exts = []
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
# File name cannot have path separators because PATH lookup does not
|
|
Packit Service |
76cb02 |
# work that way.
|
|
Packit Service |
76cb02 |
if os.sep in command or os.altsep and os.altsep in command:
|
|
Packit Service |
76cb02 |
pass
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
for i in range(len(path)):
|
|
Packit Service |
76cb02 |
dirName = path[i]
|
|
Packit Service |
76cb02 |
# On windows the dirName *could* be quoted, drop the quotes
|
|
Packit Service |
76cb02 |
if sys.platform.startswith("win") and len(dirName) >= 2\
|
|
Packit Service |
76cb02 |
and dirName[0] == '"' and dirName[-1] == '"':
|
|
Packit Service |
76cb02 |
dirName = dirName[1:-1]
|
|
Packit Service |
76cb02 |
for ext in ['']+exts:
|
|
Packit Service |
76cb02 |
absName = os.path.abspath(
|
|
Packit Service |
76cb02 |
os.path.normpath(os.path.join(dirName, command+ext)))
|
|
Packit Service |
76cb02 |
if os.path.isfile(absName):
|
|
Packit Service |
76cb02 |
if usingGivenPath:
|
|
Packit Service |
76cb02 |
fromWhere = "from given path element %d" % i
|
|
Packit Service |
76cb02 |
elif not sys.platform.startswith("win"):
|
|
Packit Service |
76cb02 |
fromWhere = "from PATH element %d" % i
|
|
Packit Service |
76cb02 |
elif i == 0:
|
|
Packit Service |
76cb02 |
fromWhere = "from current directory"
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
fromWhere = "from PATH element %d" % (i-1)
|
|
Packit Service |
76cb02 |
match = _cull((absName, fromWhere), matches, verbose)
|
|
Packit Service |
76cb02 |
if match:
|
|
Packit Service |
76cb02 |
if verbose:
|
|
Packit Service |
76cb02 |
yield match
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
yield match[0]
|
|
Packit Service |
76cb02 |
match = _getRegisteredExecutable(command)
|
|
Packit Service |
76cb02 |
if match is not None:
|
|
Packit Service |
76cb02 |
match = _cull(match, matches, verbose)
|
|
Packit Service |
76cb02 |
if match:
|
|
Packit Service |
76cb02 |
if verbose:
|
|
Packit Service |
76cb02 |
yield match
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
yield match[0]
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def which(command, path=None, verbose=0, exts=None):
|
|
Packit Service |
76cb02 |
"""Return the full path to the first match of the given command on
|
|
Packit Service |
76cb02 |
the path.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
"command" is a the name of the executable to search for.
|
|
Packit Service |
76cb02 |
"path" is an optional alternate path list to search. The default it
|
|
Packit Service |
76cb02 |
to use the PATH environment variable.
|
|
Packit Service |
76cb02 |
"verbose", if true, will cause a 2-tuple to be returned. The second
|
|
Packit Service |
76cb02 |
element is a textual description of where the match was found.
|
|
Packit Service |
76cb02 |
"exts" optionally allows one to specify a list of extensions to use
|
|
Packit Service |
76cb02 |
instead of the standard list for this system. This can
|
|
Packit Service |
76cb02 |
effectively be used as an optimization to, for example, avoid
|
|
Packit Service |
76cb02 |
stat's of "foo.vbs" when searching for "foo" and you know it is
|
|
Packit Service |
76cb02 |
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
|
Packit Service |
76cb02 |
is only supported on Windows.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
If no match is found for the command, a WhichError is raised.
|
|
Packit Service |
76cb02 |
"""
|
|
Packit Service |
76cb02 |
try:
|
|
Packit Service |
76cb02 |
match = whichgen(command, path, verbose, exts).next()
|
|
Packit Service |
76cb02 |
except StopIteration:
|
|
Packit Service |
76cb02 |
raise WhichError("Could not find '%s' on the path." % command)
|
|
Packit Service |
76cb02 |
return match
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def whichall(command, path=None, verbose=0, exts=None):
|
|
Packit Service |
76cb02 |
"""Return a list of full paths to all matches of the given command
|
|
Packit Service |
76cb02 |
on the path.
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
"command" is a the name of the executable to search for.
|
|
Packit Service |
76cb02 |
"path" is an optional alternate path list to search. The default it
|
|
Packit Service |
76cb02 |
to use the PATH environment variable.
|
|
Packit Service |
76cb02 |
"verbose", if true, will cause a 2-tuple to be returned for each
|
|
Packit Service |
76cb02 |
match. The second element is a textual description of where the
|
|
Packit Service |
76cb02 |
match was found.
|
|
Packit Service |
76cb02 |
"exts" optionally allows one to specify a list of extensions to use
|
|
Packit Service |
76cb02 |
instead of the standard list for this system. This can
|
|
Packit Service |
76cb02 |
effectively be used as an optimization to, for example, avoid
|
|
Packit Service |
76cb02 |
stat's of "foo.vbs" when searching for "foo" and you know it is
|
|
Packit Service |
76cb02 |
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
|
Packit Service |
76cb02 |
is only supported on Windows.
|
|
Packit Service |
76cb02 |
"""
|
|
Packit Service |
76cb02 |
return list( whichgen(command, path, verbose, exts) )
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
#---- mainline
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
def main(argv):
|
|
Packit Service |
76cb02 |
all = 0
|
|
Packit Service |
76cb02 |
verbose = 0
|
|
Packit Service |
76cb02 |
altpath = None
|
|
Packit Service |
76cb02 |
exts = None
|
|
Packit Service |
76cb02 |
try:
|
|
Packit Service |
76cb02 |
optlist, args = getopt.getopt(argv[1:], 'haVvqp:e:',
|
|
Packit Service |
76cb02 |
['help', 'all', 'version', 'verbose', 'quiet', 'path=', 'exts='])
|
|
Packit Service |
76cb02 |
except getopt.GetoptError, msg:
|
|
Packit Service |
76cb02 |
sys.stderr.write("which: error: %s. Your invocation was: %s\n"\
|
|
Packit Service |
76cb02 |
% (msg, argv))
|
|
Packit Service |
76cb02 |
sys.stderr.write("Try 'which --help'.\n")
|
|
Packit Service |
76cb02 |
return 1
|
|
Packit Service |
76cb02 |
for opt, optarg in optlist:
|
|
Packit Service |
76cb02 |
if opt in ('-h', '--help'):
|
|
Packit Service |
76cb02 |
print _cmdlnUsage
|
|
Packit Service |
76cb02 |
return 0
|
|
Packit Service |
76cb02 |
elif opt in ('-V', '--version'):
|
|
Packit Service |
76cb02 |
print "which %s" % __version__
|
|
Packit Service |
76cb02 |
return 0
|
|
Packit Service |
76cb02 |
elif opt in ('-a', '--all'):
|
|
Packit Service |
76cb02 |
all = 1
|
|
Packit Service |
76cb02 |
elif opt in ('-v', '--verbose'):
|
|
Packit Service |
76cb02 |
verbose = 1
|
|
Packit Service |
76cb02 |
elif opt in ('-q', '--quiet'):
|
|
Packit Service |
76cb02 |
verbose = 0
|
|
Packit Service |
76cb02 |
elif opt in ('-p', '--path'):
|
|
Packit Service |
76cb02 |
if optarg:
|
|
Packit Service |
76cb02 |
altpath = optarg.split(os.pathsep)
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
altpath = []
|
|
Packit Service |
76cb02 |
elif opt in ('-e', '--exts'):
|
|
Packit Service |
76cb02 |
if optarg:
|
|
Packit Service |
76cb02 |
exts = optarg.split(os.pathsep)
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
exts = []
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
if len(args) == 0:
|
|
Packit Service |
76cb02 |
return -1
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
failures = 0
|
|
Packit Service |
76cb02 |
for arg in args:
|
|
Packit Service |
76cb02 |
#print "debug: search for %r" % arg
|
|
Packit Service |
76cb02 |
nmatches = 0
|
|
Packit Service |
76cb02 |
for match in whichgen(arg, path=altpath, verbose=verbose, exts=exts):
|
|
Packit Service |
76cb02 |
if verbose:
|
|
Packit Service |
76cb02 |
print "%s (%s)" % match
|
|
Packit Service |
76cb02 |
else:
|
|
Packit Service |
76cb02 |
print match
|
|
Packit Service |
76cb02 |
nmatches += 1
|
|
Packit Service |
76cb02 |
if not all:
|
|
Packit Service |
76cb02 |
break
|
|
Packit Service |
76cb02 |
if not nmatches:
|
|
Packit Service |
76cb02 |
failures += 1
|
|
Packit Service |
76cb02 |
return failures
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
if __name__ == "__main__":
|
|
Packit Service |
76cb02 |
sys.exit( main(sys.argv) )
|
|
Packit Service |
76cb02 |
|
|
Packit Service |
76cb02 |
|