ทดสอบว่ามีไฟล์ปฏิบัติการใน Python หรือไม่?


297

ใน Python มีวิธีที่ง่ายและสะดวกในการทดสอบว่ามีโปรแกรมที่ทำงานได้หรือไม่?

โดยง่ายฉันหมายถึงบางสิ่งบางอย่างเช่นwhichคำสั่งซึ่งจะสมบูรณ์แบบ ฉันไม่ต้องการค้นหา PATH ด้วยตนเองหรือมีบางสิ่งที่เกี่ยวข้องกับการพยายามรันด้วยPopen& al และดูว่ามันล้มเหลว (นั่นคือสิ่งที่ฉันกำลังทำอยู่ตอนนี้ แต่ลองจินตนาการดูlaunchmissiles)


4
มีอะไรผิดปกติในการค้นหาตัวแปรสภาพแวดล้อม PATH? คุณคิดว่า UNIX 'ซึ่งคำสั่ง' ทำอะไร?
Jay

1
สคริปต์นี้ชื่อว่า.pyจาก stdlib เป็นวิธีที่ง่ายหรือไม่?
jfs

@JF - สคริปต์ which.py ​​รวม ด้วย Python ขึ้นอยู่กับ 'ls' และความคิดเห็นอื่น ๆ ระบุว่า Piotr กำลังมองหาคำตอบข้ามแพลตฟอร์ม
Jay

@ Jay: ขอบคุณสำหรับความคิดเห็น ฉันมี coreutils ติดตั้งอยู่บน Windows ดังนั้นฉันจึงไม่ได้สังเกตว่าไฟล์นั้นทำงานเฉพาะยูนิกซ์
jfs

นอกจากนี้ยังwhichมีโมดูลบุคคลที่สาม: code.activestate.com/pypm/which
Sridhar Ratnakumar

คำตอบ:


321

วิธีที่ง่ายที่สุดที่ฉันนึกได้:

def which(program):
    import os
    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file

    return None

แก้ไข : อัปเดตตัวอย่างโค้ดเพื่อรวมตรรกะสำหรับการจัดการเคสที่อาร์กิวเมนต์ที่ระบุนั้นเป็นพา ธ แบบเต็มไปยังไฟล์ที่เรียกทำงานได้แล้วเช่น "ซึ่ง / bin / ls" สิ่งนี้เลียนแบบพฤติกรรมของ UNIX 'ซึ่งคำสั่ง'

แก้ไข : อัปเดตเพื่อใช้ os.path.isfile () แทนที่จะเป็น os.path.exists () ต่อความคิดเห็น

แก้ไข : path.strip('"')ดูเหมือนผิดที่จะทำที่นี่ ไม่มี Windows หรือ POSIX ปรากฏขึ้นเพื่อสนับสนุนรายการ PATH ที่ยกมา


ขอบคุณเจย์ฉันยอมรับคำตอบของคุณ แต่สำหรับฉันแล้วมันตอบคำถามของฉันในแง่ลบ ไม่มีฟังก์ชั่นดังกล่าวอยู่ใน libs ฉันแค่ต้องเขียนมัน (ฉันยอมรับว่าสูตรของฉันไม่ชัดเจนพอในความจริงที่ว่าฉันรู้ว่าสิ่งใดที่ทำ)
Piotr Lesnicki

1
Jay, ถ้าคุณทำคำตอบของคุณให้สมบูรณ์ตามใจ (เพื่อให้มีคำว่า 'สมบูรณ์') ดังนั้นฉันสามารถลบคำตอบออกได้
Piotr Lesnicki

2
สำหรับระบบปฏิบัติการบางระบบคุณอาจต้องเพิ่มส่วนขยายที่สามารถใช้งานได้ ตัวอย่างเช่นบน Ubuntu ฉันสามารถเขียนได้ว่า ("scp") แต่ใน Windows ฉันต้องเขียนว่า ("scp.exe") ใด
waffleman

13
ฉันขอแนะนำให้เปลี่ยน "os.path.exists" เป็น "os.path.isfile" มิฉะนั้นใน Unix อาจจับคู่ไดเรกทอรีกับชุด + x บิตเท็จ ฉันยังพบว่ามีประโยชน์ในการเพิ่มสิ่งนี้ไปยังด้านบนของฟังก์ชัน: import sys; ถ้า sys.platform == "win32" และไม่ใช่ program.endswith (". exe"): program + = ".exe" วิธีนี้ใน Windows คุณสามารถอ้างถึง "calc" หรือ "calc.exe" ได้เช่นเดียวกับที่คุณทำในหน้าต่าง cmd
Kevin Ivarsen

1
@KevinIvarsen ตัวเลือกที่ดีกว่าคือการวนลูปผ่านค่าPATHEXTenv var เพราะcommandใช้ได้ตามcommand.comที่เป็นscriptvsscript.bat
Lekensteyn

325

ฉันรู้ว่านี้เป็นคำถามที่โบราณ distutils.spawn.find_executableแต่คุณสามารถใช้ สิ่งนี้ได้รับการบันทึกไว้ตั้งแต่ python 2.4และมีอยู่ตั้งแต่ python 1.6

import distutils.spawn
distutils.spawn.find_executable("notepad.exe")

นอกจากนี้หลาม 3.3 shutil.which()ในขณะนี้ข้อเสนอ


7
เมื่อวันwin32ที่distutils.spawn.find_executableดำเนินการเพียงมองหามากกว่าการใช้รายชื่อของนามสกุลเพื่อค้นหาชุดใน.exe %PATHEXT%ไม่ดี แต่อาจใช้ได้กับทุกกรณีที่มีคนต้องการ
rakslice

7
การใช้งานตัวอย่าง:from distutils import spawn php_path = spawn.find_executable("php")
codefreak

6
เห็นได้ชัดว่าdistutils.spawnไม่สามารถใช้งานได้อย่างน่าเชื่อถือ: ด้วยการติดตั้งระบบของฉัน (/ usr / bin / python) ของ Python 2.7.6 บน OS X 10.10 ฉันได้รับ: AttributeError: 'module' object has no attribute 'spawn'แม้ว่าจะใช้งานได้บนเครื่องเดียวกันกับ Python รุ่นเดียวกัน แต่จาก การติดตั้ง virtualenv
Josh Kupershmidt

8
@JoshKupershmidt ให้แน่ใจว่าการimport distutils.spawnหรือปฏิบัติตามไวยากรณ์มากกว่าแค่from distutils import spawn import distutilsมิฉะนั้นอาจไม่สามารถเข้าถึงได้และคุณจะได้รับการดังกล่าวข้างต้นAttributeErrorแม้ว่าจะมี
John St. John


39

สำหรับ python 3.2 และรุ่นก่อนหน้า:

my_command = 'ls'
any(os.access(os.path.join(path, my_command), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))

นี่เป็นหนึ่งในสายการบินของคำตอบของ Jayและที่นี่เป็น lambda func:

cmd_exists = lambda x: any(os.access(os.path.join(path, x), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))
cmd_exists('ls')

หรือท้ายที่สุด, เยื้องเป็นฟังก์ชั่น:

def cmd_exists(cmd):
    return any(
        os.access(os.path.join(path, cmd), os.X_OK) 
        for path in os.environ["PATH"].split(os.pathsep)
    )

สำหรับ python 3.3 และใหม่กว่า:

import shutil

command = 'ls'
shutil.which(command) is not None

ในฐานะที่เป็นหนึ่งซับของม.ค. ฟิลิป Gehrcke คำตอบ :

cmd_exists = lambda x: shutil.which(x) is not None

เป็น def:

def cmd_exists(cmd):
    return shutil.which(cmd) is not None

1
รุ่น "เยื้องเป็นฟังก์ชัน" ใช้ตัวแปรxที่ควรจะเป็นcmd
0x89

คุณต้องเพิ่มการทดสอบเพื่อดูว่าos.path.join(path, cmd)เป็นไฟล์หรือไม่? หลังจากนั้นไดเรกทอรียังสามารถตั้งค่าบิตที่สามารถเรียกทำงานได้ ...
MestreLion

@MestreLion ฟังดูเหมือนเป็นกรณีที่เป็นไปได้คุณจะยืนยันพฤติกรรมนี้และอัปเดตคำตอบนี้หรือไม่? ฉันยินดีที่จะเปลี่ยนโพสต์นี้เป็นวิกิชุมชนถ้ามีประโยชน์
ThorSummoner

1
@ThorSummoner: ฉันยืนยันแล้วและต้องมีการทดสอบไฟล์ การทดสอบอย่างง่าย:mkdir -p -- "$HOME"/bin/dummy && PATH="$PATH":"$HOME"/bin && python -c 'import os; print any(os.access(os.path.join(path, "dummy"), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))' && rmdir -- "$HOME"/bin/dummy
MestreLion

1
เพิ่มง่ายand os.path.isfile(...)ไปยังสถานที่ที่เหมาะสมก็เพียงพอที่จะแก้ไข
MestreLion

19

อย่าลืมระบุนามสกุลไฟล์บน windows มิฉะนั้นคุณจะต้องเขียนที่ซับซ้อนมากis_exeสำหรับ windows โดยใช้PATHEXTตัวแปรสภาพแวดล้อม คุณเพียงแค่อาจต้องการใช้FindPath

OTOH ทำไมคุณถึงต้องค้นหาไฟล์ปฏิบัติการ? ระบบปฏิบัติการจะทำเพื่อคุณโดยเป็นส่วนหนึ่งของการpopenโทร & จะเพิ่มข้อยกเว้นหากไม่พบไฟล์ปฏิบัติการ สิ่งที่คุณต้องทำคือตรวจจับข้อยกเว้นที่ถูกต้องสำหรับระบบปฏิบัติการที่ระบุ โปรดทราบว่าใน Windows subprocess.Popen(exe, shell=True)จะล้มเหลวอย่างเงียบ ๆ หากexeไม่พบ


การรวมPATHEXTเข้ากับการนำไปปฏิบัติด้านบนของwhich(ในคำตอบของเจย์):

def which(program):
    def is_exe(fpath):
        return os.path.exists(fpath) and os.access(fpath, os.X_OK) and os.path.isfile(fpath)

    def ext_candidates(fpath):
        yield fpath
        for ext in os.environ.get("PATHEXT", "").split(os.pathsep):
            yield fpath + ext

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            for candidate in ext_candidates(exe_file):
                if is_exe(candidate):
                    return candidate

    return None

1
แก้ไขข้อผิดพลาดในคำตอบที่ยอมรับรู้สึกว่าคำตอบนี้ควรอยู่ด้านบนแทน
NiTe Luo

การใช้งานที่ฉลาดyieldในการext_candidatesให้ฉันเข้าใจถึงวิธีการที่ผลงานของคำหลัก
แกรนท์ฮัมฟรีส์

15

สำหรับแพลตฟอร์ม * nix (Linux และ OS X)

ดูเหมือนว่าจะใช้งานได้สำหรับฉัน:

แก้ไขการทำงานบน Linux ด้วยMestreion

def cmd_exists(cmd):
    return subprocess.call("type " + cmd, shell=True, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0

สิ่งที่เราทำที่นี่คือการใช้คำสั่ง builtin typeและตรวจสอบรหัสออก หากไม่มีคำสั่งดังกล่าวtypeจะออกด้วย 1 (หรือรหัสสถานะที่ไม่ใช่ศูนย์)

บิตเกี่ยวกับ stdout และ stderr เป็นเพียงการเงียบผลลัพธ์ของtypeคำสั่งเนื่องจากเราสนใจเฉพาะรหัสสถานะการออก

ตัวอย่างการใช้งาน:

>>> cmd_exists("jsmin")
True
>>> cmd_exists("cssmin")
False
>>> cmd_exists("ls")
True
>>> cmd_exists("dir")
False
>>> cmd_exists("node")
True
>>> cmd_exists("steam")
False

2
คุณแน่ใจเหรอ เป็นวิธีที่ดีมาก แต่typeเป็น shell builtin ไม่ใช่ไฟล์ที่ปฏิบัติการได้จึงsubprocess.call()ล้มเหลวที่นี่
MestreLion

1
คุณได้ลองมันหรือว่าคุณกำลังทำทฤษฎีอยู่ใช่ไหม? มันใช้งานได้บน mac ของฉันอยู่แล้ว
hasen

ฉันลองใน Ubuntu 12.04 แล้วมันก็พ่นOSError: [Errno 2] No such file or directoryออกมา บางทีใน Mac typeเป็นคำสั่งจริง
MestreLion

2
หลังจากที่มากของการทดสอบที่ฉันได้พบวิธีการแก้ไข: เพิ่มshell=Trueและแทนที่["type", cmd]สำหรับ"type " + cmd
MestreLion

4
ข้อควรระวัง: ตรวจสอบให้แน่ใจว่าตัวแปร "cmd" มีข้อมูลที่ถูกต้อง ถ้ามันมาจากแหล่งภายนอกคนเลวจะให้ "ls; rm -rf /" ฉันคิดว่าวิธีการแก้ปัญหาแบบ in-python (ไม่มี subprocess) ดีกว่ามาก จุดถัดไป: ถ้าคุณเรียกใช้วิธีนี้บ่อยครั้งวิธีแก้ปัญหาของกระบวนการย่อยจะช้ากว่ามากเนื่องจากกระบวนการจำนวนมากจำเป็นต้องวางไข่
guettli

7

ดูที่โมดูลos.pathสำหรับฟังก์ชั่นที่มีประโยชน์บางอย่างเกี่ยวกับชื่อพา ธ ในการตรวจสอบว่าไฟล์ที่มีอยู่นั้นสามารถเรียกใช้งานได้หรือไม่ให้ใช้os.access (พา ธ , โหมด)ด้วยโหมด os.X_OK

os.X_OK

ค่าที่จะรวมในพารามิเตอร์โหมดของการเข้าถึง () เพื่อตรวจสอบว่าสามารถเรียกใช้พา ธ ได้หรือไม่

แก้ไข:การwhich()ใช้งานที่แนะนำจะหายไปหนึ่งเงื่อนงำ - ใช้os.path.join()เพื่อสร้างชื่อไฟล์แบบเต็ม


ขอบคุณ gimel ดังนั้นโดยพื้นฐานแล้วฉันมีคำตอบ: ไม่มีฟังก์ชั่นดังกล่าวอยู่ฉันต้องทำด้วยตนเอง
Piotr Lesnicki

อย่าใช้ os.access ฟังก์ชั่นการเข้าถึงถูกออกแบบมาสำหรับโปรแกรม suid
Changming Sun

6

บนพื้นฐานของการขอให้อภัยได้ง่ายกว่าการอนุญาตฉันจะลองใช้มันและตรวจจับข้อผิดพลาด (OSError ในกรณีนี้ - ฉันตรวจสอบว่าไม่มีไฟล์อยู่และไฟล์นั้นไม่สามารถเรียกใช้งานได้

มันจะช่วยได้ถ้าไฟล์เรียกทำงานมีบางสิ่งบางอย่างเช่น--versionแฟล็กที่ไม่มีตัวเลือกด่วน

import subprocess
myexec = "python2.8"
try:
    subprocess.call([myexec, '--version']
except OSError:
    print "%s not found on path" % myexec

นี่ไม่ใช่วิธีแก้ปัญหาทั่วไป แต่จะเป็นวิธีที่ง่ายที่สุดสำหรับกรณีการใช้งานจำนวนมาก - กรณีที่รหัสต้องการค้นหาโปรแกรมปฏิบัติการที่รู้จักกันดี


3
มันอันตรายเกินไปที่จะเรียก--versionโปรแกรมที่ชื่อว่าlaunchmissiles!
xApple

1
+1, ฉันชอบวิธีนี้ EAFP เป็นกฎ Python สีทอง ยกเว้นสำหรับการตั้งค่า UI ทำไมคุณต้องการทราบว่าlaunchmissiesมีอยู่เว้นแต่คุณต้องการเปิดตัวขีปนาวุธ ดีกว่าที่จะดำเนินการและดำเนินการตามสถานะ / ข้อยกเว้นทางออก
MestreLion

ปัญหาเกี่ยวกับวิธีนี้คือเอาต์พุตถูกพิมพ์ไปที่คอนโซล หากคุณใช้ไพพ์และเชลล์ = จริงดังนั้น OSError จะไม่เพิ่มขึ้นอีกเลย
Nick Humrich

บน macOS คุณยังมีไฟล์เรียกทำงานแบบสตับเช่นgitคุณอาจไม่ต้องการเรียกใช้แบบสุ่ม
Bob Aman

5

ฉันรู้ว่าฉันเป็นหมอผีนิดหน่อยที่นี่ แต่ฉันสะดุดในคำถามนี้และวิธีแก้ปัญหาที่ยอมรับไม่ได้ผลสำหรับฉันในทุกกรณีคิดว่าอาจเป็นประโยชน์ในการส่งต่อไป โดยเฉพาะอย่างยิ่งการตรวจจับโหมด "ปฏิบัติการ" และข้อกำหนดของการจัดหานามสกุลไฟล์ นอกจากนี้ทั้ง python3.3's shutil.which( use PATHEXT) และ python2.4 + 's distutils.spawn.find_executable(เพิ่งลองเพิ่ม'.exe') จะทำงานในเซ็ตย่อยของเคสเท่านั้น

ดังนั้นฉันจึงเขียนเวอร์ชัน "super" (ขึ้นอยู่กับคำตอบที่ยอมรับและPATHEXTข้อเสนอแนะจาก Suraj) เวอร์ชันwhichนี้ทำงานอย่างละเอียดมากขึ้นและลองใช้ชุดของเทคนิค "ความกว้าง" เป็นอันดับแรกก่อนและในที่สุดก็พยายามค้นหาที่ละเอียดยิ่งขึ้นในPATHพื้นที่:

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

การใช้งานมีลักษณะเช่นนี้:

>>> which.which("meld")
'C:\\Program Files (x86)\\Meld\\meld\\meld.exe'

วิธีการแก้ปัญหาที่ยอมรับไม่ได้สำหรับฉันในกรณีนี้เนื่องจากมีไฟล์ที่ต้องการmeld.1, meld.ico, meld.doapฯลฯ นอกจากนี้ยังมีในไดเรกทอรีหนึ่งซึ่งถูกส่งกลับมาแทน (สมมุติตั้งแต่ lexicographically แรก) เพราะการทดสอบปฏิบัติการในคำตอบที่ได้รับการยอมรับไม่สมบูรณ์และให้ บวกเท็จ



2

ฉันพบบางสิ่งใน StackOverflow ที่แก้ไขปัญหาให้ฉันได้ งานนี้จัดทำขึ้นโดยมีตัวเลือก (เช่น --help หรือ --version) ที่แสดงผลออกมาและคืนสถานะการออกเป็นศูนย์ ดูที่การระงับเอาต์พุตใน Python การเรียกไปยัง executables - "ผลลัพธ์" ที่ส่วนท้ายของ code snippet ในคำตอบนี้จะเป็นศูนย์ถ้า executable อยู่ในพา ธ มิฉะนั้นก็น่าจะเป็น 1


2

ดูเหมือนง่ายพอและใช้ได้ทั้งใน python 2 และ 3

try: subprocess.check_output('which executable',shell=True)
except: sys.exit('ERROR: executable not found')

ขออภัย Jaap แต่วิธีนี้ใช้งานได้เฉพาะเมื่อผู้ปฏิบัติการไม่เรียกรหัสออก 1 หากเรียกว่าไม่ถูกต้อง ตัวอย่างเช่นมันจะใช้งานได้กับ "dir" และ "ls" แต่ถ้าคุณดำเนินการกับบางสิ่งที่ต้องใช้การกำหนดค่ามันจะแตกแม้ว่าจะมีการดำเนินการอยู่ก็ตาม
Spedge

1
คุณต้องการคำว่า "ต้องการการกำหนดค่า" อย่างแน่นอน โดยตัวของมันเอง 'ซึ่ง' ไม่ได้ดำเนินการใด ๆ แต่เพียงตรวจสอบเส้นทางสำหรับการดำรงอยู่ของการปฏิบัติการโดยใช้ชื่อนี้ (คนที่)
jaap

1
โอ้คุณกำลังใช้ "อัน" เพื่อหาไฟล์ปฏิบัติการ ดังนั้นนี่ใช้งานได้กับ Linux / Unix เท่านั้น?
Spedge

1
ใช้command -v executableหรือtype executableเป็นสากล มีหลายกรณีที่บน Macs ไม่ส่งคืนผลลัพธ์ที่คาดหวัง
RJ

1

คำถามที่สำคัญคือ " ทำไมคุณต้องทดสอบว่ามีไฟล์ปฏิบัติการอยู่หรือไม่" บางทีคุณไม่ ;-)

เมื่อเร็ว ๆ นี้ฉันต้องการฟังก์ชั่นนี้เพื่อเปิดใช้งานวิวเวอร์สำหรับไฟล์ PNG ฉันต้องการย้ำผู้ชมที่กำหนดไว้ล่วงหน้าและเรียกใช้งานครั้งแรกที่มีอยู่ โชคดีที่ฉันเจอos.startfileกัน มันดีกว่ามาก! ง่ายพกพาสะดวกและใช้วิวเวอร์ดีฟอลต์บนระบบ:

>>> os.startfile('yourfile.png')

อัปเดต:ฉันผิดเกี่ยวกับos.startfileการพกพา ... เป็น Windows เท่านั้น สำหรับ Mac คุณต้องเรียกใช้openคำสั่ง และxdg_openบน Unix มีเป็นปัญหางูใหญ่เกี่ยวกับการเพิ่ม Mac และ Unix os.startfileการสนับสนุน



1

เพิ่มการรองรับ windows

def which(program):
    path_ext = [""];
    ext_list = None

    if sys.platform == "win32":
        ext_list = [ext.lower() for ext in os.environ["PATHEXT"].split(";")]

    def is_exe(fpath):
        exe = os.path.isfile(fpath) and os.access(fpath, os.X_OK)
        # search for executable under windows
        if not exe:
            if ext_list:
                for ext in ext_list:
                    exe_path = "%s%s" % (fpath,ext)
                    if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK):
                        path_ext[0] = ext
                        return True
                return False
        return exe

    fpath, fname = os.path.split(program)

    if fpath:
        if is_exe(program):
            return "%s%s" % (program, path_ext[0])
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return "%s%s" % (exe_file, path_ext[0])
    return None

0

คุณสามารถบอกได้ว่ามีไฟล์อยู่ในโมดูลระบบปฏิบัติการหรือไม่ โดยเฉพาะอย่างยิ่งการปฏิบัติการที่ดูเหมือนว่าจะไม่สามารถถอดได้เมื่อพิจารณาสิ่งต่าง ๆ มากมายจะสามารถใช้งานได้บนระบบปฏิบัติการ nix ที่ไม่ได้อยู่บน windows และในทางกลับกัน


0

ดูเหมือนจะเป็นตัวเลือกที่ชัดเจนคือ "ซึ่ง" แยกวิเคราะห์ผลลัพธ์ผ่านทาง popen แต่คุณสามารถจำลองมันด้วยการใช้คลาส os ใน pseudopython มันจะมีลักษณะเช่นนี้:

for each element r in path:
    for each file f in directory p:
        if f is executable:
           return True

ฉันจะระมัดระวังเกี่ยวกับการใช้คำสั่ง "ซึ่ง" ใช้ os.exec หรืออะไรแบบนั้น ไม่เพียง แต่จะช้า (ถ้าประสิทธิภาพเป็นสิ่งที่น่ากังวล) แต่ถ้าคุณใช้ตัวแปรเป็นส่วนหนึ่งของสตริง exec ความปลอดภัยจะกลายเป็นความกังวล ใครบางคนสามารถแอบใน "rm -rf /"
Parappa

1
ซึ่งเนื่องจากเราต้องการใช้ฟังก์ชัน os.popen เพื่อเรียกใช้คำสั่งที่สร้างโดยโปรแกรมจึงไม่ได้ใช้งานจริงใช่ไหม
Charlie Martin

2
ขอบคุณ แต่ฉันไม่แน่ใจว่ามี 'อันใด' อยู่บน windows และไลค์ ฉันต้องการทราบว่ามีบางสิ่งที่แฟนซีอยู่ใน lib มาตรฐาน
Piotr Lesnicki

ในการติดตั้ง Windows มาตรฐานยังคงไม่มีwhichคำสั่ง มีรุ่น UnxUtils แต่คุณต้องรู้จัก / ระบุส่วนขยายมิฉะนั้นจะไม่พบโปรแกรม
โทเบียส

0

ดังนั้นโดยทั่วไปคุณต้องการค้นหาไฟล์ในระบบไฟล์ที่ติดตั้ง (ไม่จำเป็นต้องอยู่ในไดเรกทอรี PATH เท่านั้น) และตรวจสอบว่าสามารถใช้งานได้หรือไม่ นี่แปลเป็นแผนต่อไปนี้:

  • ระบุไฟล์ทั้งหมดในระบบไฟล์ที่เมาท์แบบโลคัล
  • จับคู่ผลลัพธ์กับรูปแบบชื่อ
  • สำหรับแต่ละไฟล์ที่พบตรวจสอบว่ามันเป็นปฏิบัติการ

ฉันว่าการทำแบบพกพาจะต้องใช้พลังงานและเวลาในการคำนวณมากมาย มันเป็นสิ่งที่คุณต้องการจริงๆเหรอ?


0

มีความเป็นwhich.pyสคริปต์ในการกระจายหลามมาตรฐาน (เช่นบน Windows '\PythonXX\Tools\Scripts\which.py')

แก้ไข: which.pyขึ้นอยู่กับlsดังนั้นจึงไม่ข้ามแพลตฟอร์ม


0

ไม่มีตัวอย่างก่อนหน้าใดที่ทำงานบนทุกแพลตฟอร์ม โดยปกติแล้วพวกเขาจะล้มเหลวในการทำงานบน Windows เพราะคุณสามารถดำเนินการได้โดยไม่มีนามสกุลไฟล์และคุณสามารถลงทะเบียนส่วนขยายใหม่ ตัวอย่างเช่นบน Windows หากติดตั้ง python อย่างดีก็เพียงพอที่จะเรียกใช้งาน 'file.py' และใช้งานได้

โซลูชันที่ถูกต้องและพกพาได้เดียวที่ฉันมีคือเรียกใช้งานคำสั่งและดูรหัสข้อผิดพลาด ปฏิบัติการที่เหมาะสมใด ๆ ควรมีชุดพารามิเตอร์การโทร


-3

การใช้ห้องสมุดผ้าหลาม:

from fabric.api import *

def test_cli_exists():
    """
    Make sure executable exists on the system path.
    """
    with settings(warn_only=True):
        which = local('which command', capture=True)

    if not which:
        print "command does not exist"

    assert which

2
นี่เป็นข้อเสนอแนะที่แย่มาก คุณกำลังทำให้โปรแกรมขึ้นอยู่กับไลบรารี่การเรียกใช้งานจากระยะไกลเพื่อวางไข่โปรแกรมท้องถิ่น (ซึ่ง Python stdlib สามารถทำได้อย่างง่ายดาย) และนอกจากนี้คุณยังขึ้นอยู่กับwhich(1)ว่าไม่ได้อยู่ในระบบทั้งหมด
MichałGórny
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.