web-dev-qa-db-ja.com

Pythonに実行可能ファイルが存在するかどうかをテストしますか?

Pythonでは、実行可能プログラムが存在するかどうかをテストするための移植可能な簡単な方法はありますか?

簡単に言うと、完璧なwhichコマンドのようなものです。 PATHを手動で検索したり、Popen&alを使用して実行しようとするものを検索したり、失敗するかどうかを確認したりしません(これは私が今していることですが、launchmissilesと想像してください)

268
Piotr Lesnicki

私が考えることができる最も簡単な方法:

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アイテムを推奨していないようです。

312
Jay

これは古代の質問ですが、distutils.spawn.find_executableを使用できます。これは python 2.4以降に文書化 であり、python 1.6以降に存在します。

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

また、Python 3.3は shutil.which() を提供するようになりました。

291
Nathan Binkert

Python 3.3で shutil.which() が提供されるようになりました。

126

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

python 3.3以降の場合:

import shutil

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

Jan-Philip Gehrcke Answer のワンライナーとして:

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

Defとして:

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

Windowsでファイル拡張子を指定することを忘れないでください。それ以外の場合は、PATHEXT環境変数を使用して、Windows用に非常に複雑なis_exeを記述する必要があります。単に FindPath を使用することもできます。

OTOH、なぜあなたは実行可能ファイルを検索することさえ気にしているのですか?オペレーティングシステムはpopen呼び出しの一部としてそれを行い、実行可能ファイルが見つからない場合は例外を発生させます。必要なことは、特定のOSの正しい例外をキャッチすることだけです。 Windowsでは、exeが見つからない場合、subprocess.Popen(exe, Shell=True)はサイレントに失敗します。


PATHEXTを上記のwhichの実装に組み込む(Jayの答え):

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
18
Suraj

* nixプラットフォームの場合(LinuxおよびOS X)

これは私のために働いているようです:

Linux上で動作するように編集されましたMestreion のおかげ

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

ここで行っているのは、組み込みコマンド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
16
hasen

パス名に関する便利な関数については、 os.path moduleを参照してください。既存のファイルが実行可能かどうかを確認するには、os.X_OKモードで os.access(path、mode) を使用します。

os.X_OK

パスを実行できるかどうかを判断するために、access()のmodeパラメーターに含める値。

EDIT:推奨されるwhich()実装には、1つの手がかりがありません- os.path.join() を使用して完全なファイル名を作成します。

7
gimel

それが 許可よりも許しやすい に基づいて、私はそれを使用してエラーをキャッチしようとします(この場合はOSError-ファイルが存在しないことを確認し、ファイルは実行可能ではありませんどちらもOSErrorを返します)。

実行可能ファイルに--versionフラグのようなものがあり、それが簡単なノーオペレーションである場合に役立ちます。

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

これは一般的な解決策ではありませんが、多くのユースケース-コードが単一のよく知られている実行可能ファイルを探す必要がある場合に最も簡単な方法です。

5
Hamish Downer

私はここで少しネクロマンサーになっていることを知っていますが、私はこの質問に出くわし、受け入れられた解決策はすべての場合にうまくいきませんでした。特に、「実行可能」モードの検出、およびファイル拡張子の指定要件。さらに、python3.3のshutil.whichPATHEXTを使用)とpython2.4 +のdistutils.spawn.find_executable'.exe'を追加しようとするだけ)は、一部のケースでのみ機能します。

それで、私は「受け入れられた答えとスラジュからのPATHEXT提案に基づいて」「スーパー」バージョンを書きました。 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.1meld.icomeld.doapなどのようなファイルもディレクトリにあり、そのうちの1つが代わりに返されたからです(おそらく辞書式に最初に)不完全で誤検知を与えます。

5
Preet Kukreti

最良の例は、pythonのPython bulit-inモジュールshutil.which()であるべきです。3.リンクは https://hg.python.org/cpythonです/file/default/Lib/shutil.py

4
liuyix

StackOverflowで問題を解決する何かを見つけました。これは、実行可能ファイルに何かを出力し、ゼロの終了ステータスを返すオプション(--helpや--versionなど)があれば機能します。 Python実行可能ファイルへの呼び出しの出力を抑制する -実行可能ファイルがパスにある場合、この回答のコードスニペットの最後の「結果」はゼロになります。 1になりそうです。

2
Somesh

これは非常にシンプルに思え、python 2と3の両方で動作します

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

「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
jung rhew

重要な質問は、「なぜ実行可能ファイルが存在するかどうかをテストする必要があるのですか?」です。たぶんあなたはしませんか? ;-)

最近、PNGファイルのビューアを起動するためにこの機能が必要になりました。事前に定義されたいくつかのビューアを反復処理し、最初に存在するビューアを実行したかったのです。幸いなことに、私はos.startfileに出会いました。はるかに良いです!シンプルで移植性があり、システムでdefaultビューアーを使用します。

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

更新:os.startfileの移植性が間違っていました... Windowsのみです。 Macでは、openコマンドを実行する必要があります。 Unixの場合はxdg_openos.startfileのMacおよびUnixサポートを追加する際に Pythonの問題 があります。

1
ash

追加された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
1
wukong

以前の例はすべてのプラットフォームで機能しません。通常、ファイル拡張子andなしで実行できるため、Windowsでは機能しません。新しい拡張子を登録できます。たとえば、Windowsでpythonが適切にインストールされている場合、 'file.py'を実行するだけで機能します。

私が持っていた唯一の有効でポータブルなソリューションは、コマンドを実行してエラーコードを表示することでした。適切な実行可能ファイルには、何も実行しない一連の呼び出しパラメーターが必要です。

0
sorin

osモジュールにファイルが存在するかどうかを確認できます。特に、実行可能ファイルは、Windowsではなくnixで実行可能であり、その逆も同様であるため、非常に移植性が低いようです。

0
Dustin Getz

明らか選択は "which"で、popenを介して結果を解析しているように見えますが、osクラスを使用してそれ以外の方法でシミュレートできます。 pseudopythonでは、次のようになります。

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

したがって、基本的には、マウントされたファイルシステム(必ずしもPATHディレクトリのみである必要はありません)でファイルを検索し、実行可能かどうかを確認します。これは、次の計画に変換されます。

  • ローカルにマウントされたファイルシステムのすべてのファイルを列挙する
  • 結果を名前パターンと一致させる
  • 見つかったファイルごとに、実行可能かどうかを確認します

ポータブルな方法でこれを行うには、多くの計算能力と時間が必要になります。本当に必要なものですか?

0
zgoda

標準のPythonディストリビューションには which.py​​ スクリプトがあります(例:Windows '\PythonXX\Tools\Scripts\which.py')。

編集:which.pylsに依存するため、クロスプラットフォームではありません。

0
jfs