import os import sys def which(cmd, mode=os.F_OK | os.X_OK, path=None): """Given a command, mode, and a PATH string, return the path which conforms to the given mode on the PATH, or None if there is no such file. `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result of os.environ.get("PATH"), or can be overridden with a custom search path. """ # Check that a given file can be accessed with the correct mode. # Additionally check that `file` is not a directory, as on Windows # directories pass the os.access check. def _access_check(fn, mode): return (os.path.exists(fn) and os.access(fn, mode) and not os.path.isdir(fn)) # Check files in "dir" for a match in our list of OK files. def _check_files(dir, mode): for thefile in files: name = os.path.join(dir, thefile) if _access_check(name, mode): return name return None is_windows = sys.platform == "win32" if is_windows: # PATHEXT is necessary to check on Windows. pathext = [ext.lower() for ext in os.environ.get("PATHEXT", "").split(os.pathsep)] # See if the given file matches any of the expected path extensions. # This will allow us to short circuit when given "python.exe". # If it does match, only test that one, otherwise we have to try # others. cmd_lower = cmd.lower() if any(cmd_lower.endswith(ext) for ext in pathext): files = [cmd] else: files = [cmd + ext for ext in pathext] else: files = [cmd] # If we're given a path with a directory part, look it up directly rather # than referring to PATH directories. This includes checking relative to the # current directory, e.g. ./script dirname = os.path.dirname(cmd) if dirname: return _check_files(dirname, mode) if path is None: path = os.environ.get("PATH", os.defpath) if not path: return None path = path.split(os.pathsep) if is_windows: # The current directory takes precedence on Windows. if not os.curdir in path: path.insert(0, os.curdir) seen = set() for dir in path: normdir = os.path.normcase(dir) if not normdir in seen: seen.add(normdir) thefile = _check_files(dir, mode) if thefile: return thefile return None if __name__ == "__main__": args = sys.argv[1:] if not (1 <= len(args) <= 2): sys.exit("Args: command [path-string]") print(which(args[0], path=args[1] if len(args) > 1 else None))