Tôi biết rằng tôi là một người cần thiết ở đây, nhưng tôi tình cờ thấy câu hỏi này và giải pháp được chấp nhận không phù hợp với tôi trong mọi trường hợp. Dù sao đi nữa, nó cũng có thể hữu ích. Cụ thể, phát hiện chế độ "thực thi" và yêu cầu cung cấp phần mở rộng tệp. Hơn nữa, cả python3.3 shutil.which
(sử dụng PATHEXT
) và python2.4 + distutils.spawn.find_executable
(chỉ cố gắng thêm '.exe'
) chỉ hoạt động trong một tập hợp con của các trường hợp.
Vì vậy, tôi đã viết một phiên bản "siêu" (dựa trên câu trả lời được chấp nhận và PATHEXT
gợi ý từ Suraj). Phiên bản which
này thực hiện nhiệm vụ kỹ lưỡng hơn một chút và thử một loạt các kỹ thuật đầu tiên "broadphase", và cuối cùng thử các tìm kiếm chi tiết hơn trên PATH
không gian:
import os
import sys
import stat
import tempfile
def is_case_sensitive_filesystem():
tmphandle, tmppath = tempfile.mkstemp()
is_insensitive = os.path.exists(tmppath.upper())
os.close(tmphandle)
os.remove(tmppath)
return not is_insensitive
_IS_CASE_SENSITIVE_FILESYSTEM = is_case_sensitive_filesystem()
def which(program, case_sensitive=_IS_CASE_SENSITIVE_FILESYSTEM):
""" Simulates unix `which` command. Returns absolute path if program found """
def is_exe(fpath):
""" Return true if fpath is a file we have access to that is executable """
accessmode = os.F_OK | os.X_OK
if os.path.exists(fpath) and os.access(fpath, accessmode) and not os.path.isdir(fpath):
filemode = os.stat(fpath).st_mode
ret = bool(filemode & stat.S_IXUSR or filemode & stat.S_IXGRP or filemode & stat.S_IXOTH)
return ret
def list_file_exts(directory, search_filename=None, ignore_case=True):
""" Return list of (filename, extension) tuples which match the search_filename"""
if ignore_case:
search_filename = search_filename.lower()
for root, dirs, files in os.walk(path):
for f in files:
filename, extension = os.path.splitext(f)
if ignore_case:
filename = filename.lower()
if not search_filename or filename == search_filename:
yield (filename, extension)
break
fpath, fname = os.path.split(program)
# is a path: try direct program path
if fpath:
if is_exe(program):
return program
elif "win" in sys.platform:
# isnt a path: try fname in current directory on windows
if is_exe(fname):
return program
paths = [path.strip('"') for path in os.environ.get("PATH", "").split(os.pathsep)]
exe_exts = [ext for ext in os.environ.get("PATHEXT", "").split(os.pathsep)]
if not case_sensitive:
exe_exts = map(str.lower, exe_exts)
# try append program path per directory
for path in paths:
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
# try with known executable extensions per program path per directory
for path in paths:
filepath = os.path.join(path, program)
for extension in exe_exts:
exe_file = filepath+extension
if is_exe(exe_file):
return exe_file
# try search program name with "soft" extension search
if len(os.path.splitext(fname)[1]) == 0:
for path in paths:
file_exts = list_file_exts(path, fname, not case_sensitive)
for file_ext in file_exts:
filename = "".join(file_ext)
exe_file = os.path.join(path, filename)
if is_exe(exe_file):
return exe_file
return None
Cách sử dụng trông như thế này:
>>> which.which("meld")
'C:\\Program Files (x86)\\Meld\\meld\\meld.exe'
Các giải pháp được chấp nhận không làm việc cho tôi trong trường hợp này, vì đã có các file thích meld.1
, meld.ico
, meld.doap
, vv cũng trong thư mục, một trong số đó đã được trả lời thay vì (có lẽ kể từ thứ tự từ điển đầu tiên) vì kiểm tra thực thi trong câu trả lời được chấp nhận là không đầy đủ và đưa ra dương tính giả.