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


297

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

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


4
PATH環境変数の検索の何が問題になっていますか?UNIXの「which」コマンドは何をしていると思いますか?
ジェイ

1
stdlibのwhich.py​​スクリプトは簡単な方法ですか?
jfs 2008

@JF-which.py​​スクリプトを含む。Pythonでは 'ls'に依存し、他のコメントのいくつかは、Piotrがクロスプラットフォームの回答を探していたことを示しています。
ジェイ

@ジェイ:コメントありがとうございます。私はWindowsにcoreutilsをインストールしているので、which.py​​がUNIX固有であることに気付きませんでした。
jfs 2008

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


ジェイに感謝します。私はあなたの答えを受け入れますが、私にとっては否定的な質問で私の質問に答えます。そのような関数はライブラリには存在しません。私はそれを書かなければなりません(自分の公式が何をするのかを知っているという事実において、私の公式は十分に明確ではなかったことを認めます)。
Piotr Lesnicki 2008

1
ジェイ、あなたが私の答えを完成させたなら(完全な 'w'を持っている)私は私のものを削除することができます
Piotr Lesnicki 2008

2
一部のOSでは、実行可能ファイルの拡張子を追加する必要がある場合があります。たとえば、Ubuntuではwhich( "scp")と記述できますが、Windowsではwhich( "scp.exe")と記述する必要があります。
ワッフルマン

13
「os.path.exists」を「os.path.isfile」に変更することをお勧めします。そうでない場合、Unixでは、+ xビットが設定されたディレクトリと誤って一致する可能性があります。これを関数の先頭に追加すると便利です。importsys; sys.platform == "win32"で、program.endswith( "。exe")ではない場合:プログラム+ = ".exe"。このように、Windowsでは、cmdウィンドウと同じように、「calc」または「calc.exe」のいずれかを参照できます。
Kevin Ivarsen、2011

1
@KevinIvarsen Aより良いオプションは、の値をループになる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 2013年

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があり、そこにある場合でも上記を取得します。
ジョンセントジョン


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's Answerの 1行であり、ラムダ関数としてもここにあります。

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

1
「関数としてインデント」バージョンでは、変数を使用するx必要がありますcmd
0x89

os.path.join(path, cmd)ファイルかどうかを確認するためのテストも追加する必要がありますか?結局のところ、ディレクトリには実行可能ビットを設定することもできます...
MestreLion

@MestreLion考えられるケースのように聞こえますが、この動作を確認してこの回答を更新していただけますか?それが役に立った場合、私はこの投稿をコミュニティーwikiに変更できてうれしいです。
ThorSummoner 2016年

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 2016年

1
and os.path.isfile(...)適切な場所に単純なものを追加するだけでそれを修正できます
MestreLion

19

Windowsでファイル拡張子を指定することを忘れないでください。それ以外の場合は、環境変数is_exeを使用して、Windowsに対して非常に複雑なコードを記述する必要があり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 2017

yieldinを巧みに使用ext_candidatesすることで、そのキーワードがどのように機能するかをよりよく理解できました
Grant Humphries

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(またはゼロ以外のステータスコード)で終了します。

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 2013年

1
あなたはそれを試しましたか、それとも理論化していますか?それはとにかく私のMacで動作します。
Hasen 2013年

Ubuntu 12.04で試してみましたが、スローされOSError: [Errno 2] No such file or directoryます。多分Mac typeで実際のコマンド
MestreLion 2013年

2
LOT追加:テスト、私は修正する方法を見つけたshell=Trueと置き換える["type", cmd]ために"type " + cmd
MestreLion

4
注意:変数「cmd」に有効なデータが含まれていることを確認してください。それが外部ソースからのものである場合、悪者はあなたに「ls; rm -rf /」を与えることができます。Python内のソリューション(サブプロセスなし)ははるかに優れていると思います。次のポイント:このメソッドを頻繁に呼び出すと、多くのプロセスを生成する必要があるため、サブプロセスソリューションははるかに遅くなります。
guettli 2013

7

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

os.X_OK

パスを実行できるかどうかを決定するためにaccess()のモードパラメータに含める値。

編集:提案which()された実装には1つの手掛かりがありません- os.path.join()完全なファイル名を構築するために使用します。


おかげで、ギメル、基本的には私の答えがあります。そのような機能は存在しないので、手動で実行する必要があります。
Piotr Lesnicki 2008

os.accessは使用しないでください。アクセス機能は、suidプログラム用に設計されています。
Changming Sun

6

許可よりも許しを求める方簡単なので、私はそれを使用してエラーをキャッチしようとします(この場合はOSError-ファイルが存在しないことを確認し、ファイルが実行可能ではなく、どちらも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

この方法の問題は、出力がコンソールに出力されることです。パイプとshell = Trueを使用する場合、OSErrorは発生しません
Nick Humrich

macOSには、たとえばgitブラインドで実行したくない場合のためのスタブ実行可能ファイルもあります。
Bob Aman 2017年

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.1meld.icomeld.doap、なども受け入れ答えで実行可能なテストが不完全と与えていたので(辞書順で最初以来、おそらく)代わりに返されたそのうちの一つのディレクトリ、中偽陽性。



2

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

1
「構成が必要」とはどういう意味ですか?それ自体は実際には何も実行せず、この名前で実行可能ファイルが存在するかどうかをPATHで確認するだけです(man which)。
jaap

1
ああ、実行可能ファイルを見つけるために「which」を使用しています。これはLinux / Unixでのみ機能しますか?
Spedge 2015年

1
command -v executableまたはtype executableを使用して、普遍的です。Macでは、期待した結果が返されない場合があります。
RJ

1

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

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

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

更新:os.startfile移植性に問題がありました... Windowsのみです。Macでは、openコマンドを実行する必要があります。そしてxdg_openUnixで。MacおよびUnixのサポートの追加に関するPythonの問題があり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

osモジュールでファイルが存在するかどうかを確認できます。特に実行可能ファイルは、多くのものがウィンドウズ上ではなくnix上で実行可能であり、その逆も同様であることを考えると、かなり移植性がないようです。


0

popenを介して結果を解析する「どちらか」を選択するのは明らかなようですが、それ以外の場合はosクラスを使用してシミュレートできます。疑似Pythonでは、次のようになります。

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 /」に忍び込むことができます。
パラッパ

1
os.popen関数を使用してプログラムによって作成されたwhichコマンドを実行するため、実際には適用されません。
チャーリーマーティン

2
ありがとう、でも「どっち」がウィンドウなどに存在するのかわかりません。私は基本的に、標準ライブラリに奇妙なものが存在するかどうかを知りたいと思っていました
Piotr Lesnicki '18

標準のWindowsインストールでは、まだwhichコマンドはありません。UnxUtilsバージョンがありますが、拡張機能を知っている/指定する必要があります。そうしないと、プログラムが見つかりません。
Tobias

0

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

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

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


0

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

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


0

これまでの例はすべてのプラットフォームで機能しません。通常、彼らはあなたがファイル拡張子なしで実行することができますので、Windows上で動作するように失敗する、新しい拡張機能を登録できること。たとえば、Windowsでは、Pythonが適切にインストールされている場合、「file.py」を実行するだけで十分に機能します。

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


-3

Pythonファブリックライブラリの使用:

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.