파이썬에 실행 파일이 있는지 테스트 하시겠습니까?


297

파이썬에서 실행 가능한 프로그램이 존재하는지 테스트하는 이식 가능하고 간단한 방법이 있습니까?

간단히 말해서 나는 which완벽한 명령 과 같은 것을 의미합니다 . PATH를 수동으로 검색하거나 Popen& al 을 사용하여 PATH를 실행 하고 실패하는지 확인하고 싶지 않습니다 (지금 내가하고있는 일이지만 상상해보십시오 launchmissiles)


4
PATH 환경 변수 검색에 어떤 문제가 있습니까? 유닉스 'which'명령은 무엇이라고 생각하십니까?
Jay

1
stdlib의 which.py ​​스크립트는 간단한 방법입니까?
jfs

@JF-which.py ​​스크립트 포함 파이썬에서 'ls'에 의존하고 다른 의견 중 일부는 Piotr이 크로스 플랫폼 답변을 찾고 있음을 나타냅니다.
Jay

@Jay : 댓글 주셔서 감사합니다. Windows에 coreutils가 설치되어 있으므로 which.py가 유닉스 전용이라는 것을 알지 못했습니다.
jfs

또한이 which타사 모듈 : code.activestate.com/pypm/which는
스리 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

편집 : 제공된 인수가 이미 실행 파일의 전체 경로 인 경우 (예 : "which / bin / ls")를 처리하기위한 논리를 포함하도록 코드 샘플이 업데이트되었습니다. 이것은 UNIX 'which'명령의 동작을 모방합니다.

편집 : 주석마다 os.path.exists () 대신 os.path.isfile ()을 사용하도록 업데이트되었습니다.

편집 : path.strip('"')여기에 잘못한 것처럼 보입니다. Windows 나 POSIX 모두 인용 된 PATH 항목을 권장하지 않습니다.


고마워 제이, 나는 당신의 대답을 받아들입니다. 그러한 함수는 libs에 존재하지 않으며 그냥 작성해야합니다 (내가 무엇을하는지 알기 때문에 내 공식이 명확하지 않다는 것을 인정합니다).
Piotr Lesnicki

1
제이, 내 말에 따라 답을 완성하면 ( 'w'를 완수하려면) 내 것을 제거 할 수 있습니다.
Piotr Lesnicki

2
일부 OS의 경우 실행 파일 확장자를 추가해야 할 수도 있습니다. 예를 들어, 우분투에서는 which ( "scp")를 쓸 수 있지만 Windows에서는 which ( "scp.exe")를 쓸 필요가있었습니다.
waffleman

13
"os.path.exists"를 "os.path.isfile"로 변경하는 것이 좋습니다. 그렇지 않으면 Unix에서는 + x 비트가 설정된 디렉토리와 잘못 일치 할 수 있습니다. 또한 함수 맨 위에 이것을 추가하는 것이 유용하다는 것을 알았습니다. import sys; sys.platform == "win32"이고 program.endswith ( ". exe")가 아닌 경우 : program + = ".exe". 이 방법으로 Windows에서는 cmd 창에서와 같이 "calc"또는 "calc.exe"를 참조 할 수 있습니다.
Kevin Ivarsen

1
@KevinIvarsen 더 나은 옵션의 값을 통해 반복 것 PATHEXT때문에 ENV var에 command같은 유효한 경우 command.com와 같이 scriptscript.bat
Lekensteyn

325

나는 이것이 고대의 질문이라는 것을 알고 있지만 사용할 수 있습니다 distutils.spawn.find_executable. 이것은 python 2.4 이후문서화 되었으며 python 1.6 이후로 존재했습니다.

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

또한 Python 3.3은 다음을 제공합니다. shutil.which() .


7
에서 win32distutils.spawn.find_executable구현은 단지를 찾는 .exe대신에 세트를 검색 할 확장명 목록을 사용하는 것보다 %PATHEXT%. 그것은 좋지는 않지만 누군가가 필요로하는 모든 경우에 효과적 일 수 있습니다.
rakslice

7
사용법 예 :from distutils import spawn php_path = spawn.find_executable("php")
codefreak

6
distutils.spawnOS X 10.10에서 Python 2.7.6의 시스템 설치 (/ usr / bin / python)를 사용하면 분명히 AttributeError: 'module' object has no attribute 'spawn'동일한 버전의 Python을 사용하는 동일한 컴퓨터에서 작동하지만 확실하게 사용할 수는 없습니다. virtualenv 설치
Josh Kupershmidt 2016 년

8
@JoshKupershmidt, import distutils.spawn또는 from distutils import spawn그냥보다는 구문을 따르십시오 import distutils. 그렇지 않으면 액세스 할 수 없으며 위와 AttributeError같은 경우에도 위의 정보를 얻을 수 있습니다.
John St. John


39

파이썬 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 's Answer의 한 줄짜리이며 람다 함수로도 사용됩니다.

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)
    )

파이썬 3.3 이상의 경우 :

import shutil

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

의 1 라이너로 월 - 필립 Gehrcke 응답 :

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

데프로서 :

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사용하여 창에 훨씬 복잡한 것을 작성해야 PATHEXT합니다. FindPath 만 사용하고 싶을 수도 있습니다 .

OTOH, 왜 실행 파일 검색을 귀찮게합니까? 운영 체제는 popen호출의 일부로 사용자를 대신 하여 실행 파일을 찾지 못하면 예외를 발생시킵니다. 주어진 OS에 대해 올바른 예외를 잡기 만하면됩니다. 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)

이것은 나를 위해 일하는 것 같습니다 :

Mestreion 덕분에 Linux에서 작동하도록 편집

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

여기서하는 것은 내장 명령을 사용 type하고 종료 코드를 확인하는 것입니다. 그러한 명령이 없다면type 1 (또는 0이 아닌 상태 코드)으로 종료됩니다.

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실행 파일이 아닌 쉘 내장 기능이므로 subprocess.call()여기서 실패합니다.
MestreLion

1
당신은 그것을 시도 했습니까 아니면 그냥 이론화하고 있습니까? 어쨌든 내 Mac에서 작동합니다.
hasen

우분투 12.04에서 시도했지만 던졌습니다 OSError: [Errno 2] No such file or directory. 아마도 Mac type에서는 실제 명령 일 것입니다
MestreLion

2
LOT 추가 : 시험, 어떻게 수정을 발견했습니다 shell=True및 교체 ["type", cmd]를 위해"type " + cmd
MestreLion

4
주의 : 변수 "cmd"에 유효한 데이터가 포함되어 있는지 확인하십시오. 외부 소스에서 온 경우 나쁜 사람이 "ls; rm -rf /"를 줄 수 있습니다. 파이썬 내 솔루션 (하위 프로세스 없음)이 훨씬 낫습니다. 다음 포인트 :이 메소드를 자주 호출하면 많은 프로세스를 생성해야하므로 하위 프로세스 솔루션이 훨씬 느려집니다.
guettli

7

경로 이름에 대한 유용한 기능 은 os.path 모듈을 참조하십시오 . 기존 파일이 실행 가능한지 확인하려면 os.X_OK 모드와 함께 os.access (path, mode) 를 사용하십시오.

os.X_OK

path를 실행할 수 있는지 판별하기 위해 access ()의 mode 매개 변수에 포함 할 값입니다.

편집 : 제안 된 which()구현에는 os.path.join()전체 파일 이름을 작성 하는 데 사용 되는 단서가 하나 없습니다 .


고마워, gimel 기본적으로 내 대답이 있습니다. 그러한 기능이 없으므로 수동으로해야합니다.
Piotr Lesnicki

os.access를 사용하지 마십시오. 액세스 기능은 suid 프로그램을 위해 설계되었습니다.
Changming Sun

6

허가보다 용서를 구하는 것이 더 쉽다 는 기준으로 사용하려고 시도하고 오류를 잡으려고합니다 (이 경우 OSError-파일을 확인하지 않았으며 파일을 실행할 수 없으며 둘 다 OSError를 나타냅니다).

실행 파일 --version에 빠른 no-op 플래그 와 같은 것이 있으면 도움이됩니다 .

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는 황금 파이썬 규칙입니다. UI 설정을 제외하고 launchmissies미사일을 발사하지 않는 한 왜 존재 하는지 알고 싶습니까? 그것을 실행하고 종료 상태 / 예외에 따라 행동하는 것이
좋습니다

이 방법의 문제점은 출력이 콘솔에 인쇄된다는 것입니다. 파이프와 shell = True를 사용하면 OSError가 발생하지 않습니다.
Nick Humrich

macOS에는 git맹목적으로 실행하고 싶지 않은 스텁 실행 파일도 있습니다 .
Bob Aman

5

나는 여기에서 약간의 사악함을 알고 있지만이 질문을 우연히 발견했으며 모든 경우에 받아 들여진 해결책이 효과가 없었습니다. 어쨌든 제출하는 것이 도움이 될 것이라고 생각했습니다. 특히, "실행 가능"모드 감지 및 파일 확장자 제공 요구 사항. 또한 python3.3 shutil.which( PATHEXT)과 python2.4 + distutils.spawn.find_executable( '.exe') 만 추가 하면 일부 경우에만 작동합니다.

그래서 나는 "슈퍼"버전을 썼습니다 (허용 된 답변과 PATHEXTSuraj 의 제안 에 기초하여 ). 이 버전은 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, 등 또한 허용 대답에서 실행 테스트가 불완전하고 제공했기 때문에 (전적으로 첫번째 때문에 아마도) 대신에 반환 된 하나의 디렉토리에 오 탐지.



2

StackOverflow에서 문제를 해결 한 것을 발견했습니다. 실행 파일에 무언가를 출력하고 종료 상태 0을 반환하는 옵션 (--help 또는 --version과 같은)이 있으면 작동합니다. 실행 파일에 대한 Python 호출에서 출력 억제를 참조 하십시오 . 실행 파일이 경로에 있으면이 응답의 코드 스 니펫 끝에있는 "결과"는 0이되고 그렇지 않으면 1이됩니다.


2

이것은 간단하게 보이고 파이썬 2와 3에서 모두 작동합니다.

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

죄송하지만 Jaap이지만이 솔루션은 실행 파일이 잘못 호출 된 경우 종료 코드 1을 호출하지 않는 경우에만 작동합니다. 예를 들어, "dir"및 "ls"에 대해 작동하지만 구성이 필요한 항목에 대해 실행하면 실행 파일이 있어도 중단됩니다.
Spedge

1
"구성 필요"란 정확히 무엇을 의미합니까? 자체적으로 '어떤 것'도 실제로 아무것도 실행하지 않고 단지 PATH에서이 이름으로 실행 파일이 있는지 확인합니다 (man which).
jaap

1
Ohh, 실행 파일을 찾기 위해 "which"를 사용하고 있습니다. 그렇다면 이것은 Linux / Unix에서만 작동합니까?
Spedge

1
command -v executable또는 type executable보편적으로 사용하십시오 . Mac에서 예상 결과를 반환하지 않는 경우가 있습니다.
RJ

1

중요한 질문은 " 실행 파일이 존재하는지 테스트해야합니까?"입니다. 어쩌면 당신은하지 않습니까? ;-)

최근 PNG 파일 뷰어를 시작하려면이 기능이 필요했습니다. 사전 정의 된 일부 뷰어를 반복하고 존재하는 첫 번째 뷰어를 실행하고 싶었습니다. 다행히도 나는 왔습니다 os.startfile. 훨씬 낫다! 간단하고 휴대 가능하며 시스템 에서 기본 뷰어를 사용합니다 .

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

업데이트 :os.startfile 휴대용 이 잘못되었습니다 ... Windows 만입니다. Mac에서는 open명령 을 실행해야합니다 . 그리고 xdg_open유닉스에서. 에 대한 Mac 및 Unix 지원 추가에 대한 Python 문제 가 있습니다 os.startfile.


1

"sh"( http://amoffat.github.io/sh/ ) 라는 외부 라이브러리를 사용해 볼 수 있습니다 .

import sh
print sh.which('ls')  # prints '/bin/ls' depending on your setup
print sh.which('xxx') # prints None

1

추가 된 윈도우 지원

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

os 모듈에 파일이 있는지 알 수 있습니다. 특히 실행 파일은 많은 것이 Windows에서 실행되지 않는 nix에서 실행 가능하고 그 반대의 경우를 고려할 때 상당히 이식 가능하지 않은 것 같습니다.


0

그것은 보인다 분명 "어떤"는 popen을 통해 결과를 분석 선택이 될 것입니다하지만 당신은 그렇지 않으면 운영 체제 클래스를 사용하여 시뮬레이션 할 수 있습니다. pseudopython에서는 다음과 같습니다.

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

os.exec 또는 이와 유사한 것을 사용하여 "which"명령을 실행하는 것에주의해야합니다. 성능이 문제가 될 경우 속도가 느릴뿐 아니라 변수를 exec 문자열의 일부로 사용하는 경우 보안이 문제가됩니다. 누군가 "rm -rf /"에 몰래 들어갈 수 있습니다.
Parappa

1
os.popen 함수를 사용하여 프로그램에서 생성 한 명령을 실행했기 때문에 실제로 적용되지 않는 것은 무엇입니까?
Charlie Martin

2
고마워하지만 창문과 좋아요에 '어떤'이 있는지 확실하지 않습니다. 뭔가 공상가의 standart lib에 존재하는지 알고 본질적으로 원
표트르 Lesnicki에게

표준 Windows 설치에서는 여전히 which명령 이 없습니다 . UnxUtils 버전이 있지만 확장명을 알고 / 지정해야합니다. 그렇지 않으면 프로그램을 찾을 수 없습니다.
Tobias

0

따라서 기본적으로 마운트 된 파일 시스템에서 파일을 찾고 (필수 PATH 디렉토리에서만) 파일이 실행 가능한지 확인하려고합니다. 이것은 다음 계획으로 해석됩니다.

  • 로컬로 마운트 된 파일 시스템의 모든 파일을 열거
  • 이름 패턴과 결과 일치
  • 발견 된 각 파일에 대해 실행 파일인지 확인하십시오.

휴대용 방식으로이 작업을 수행하려면 많은 컴퓨팅 성능과 시간이 필요합니다. 정말 필요한가요?


0

표준 Python 배포판 에는 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.