Python'da yürütülebilir olup olmadığını test ettiniz mi?


297

Python'da yürütülebilir bir program olup olmadığını test etmenin taşınabilir ve basit bir yolu var mı?

Basitçe söylemek gerekirse, whichsadece mükemmel olacak komut gibi bir şey demek istiyorum . PATH'yi manuel olarak veya Popen& al ile yürütmeyi denemek ve başarısız olup olmadığını görmek isteyen bir şey istemiyorum (şimdi yaptığım şey bu, ama hayal edin launchmissiles)


4
PATH ortam değişkenini aramanın nesi yanlış? UNIX 'hangi' komutunun ne yaptığını düşünüyorsunuz?
Jay

1
Stdlib'den hangi.py betiği basit bir yol mu?
jfs

@JF - which.py ​​komut dosyası dahil. Python ile 'ls' bağlıdır ve diğer bazı yorumlar Piotr platformlar arası bir cevap aradığını göstermektedir.
Jay

@Jay: Yorum için teşekkürler. Windows'ta coreutils yüklü, bu yüzden hangi.py unix-özgü olduğunu fark etmedi.
jfs

Ayrıca which, üçüncü taraf modülü de var: code.activestate.com/pypm/which
Sridhar Ratnakumar

Yanıtlar:


321

Düşünebileceğim en kolay yol:

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

Düzenleme : Sağlanan bağımsız değişken zaten çalıştırılabilir, yani "hangi / bin / ls" tam bir yol olduğu durumda işleme için mantık içerecek şekilde kod örneği güncelleştirildi. Bu UNIX 'hangi' komutunun davranışını taklit eder.

Düzenleme : Yorumlar başına os.path.exists () yerine os.path.isfile () kullanılacak şekilde güncellendi.

Düzenleme : path.strip('"')burada yapmak yanlış bir şey gibi görünüyor. Ne Windows ne de POSIX, alıntı yapılan PATH öğelerini teşvik etmemektedir.


Teşekkürler Jay, cevabını kabul ediyorum, ama benim için sorumu olumsuz yanıtlıyor. Libs'de böyle bir işlev yoktur, sadece yazmak zorundayım (formülasyonun hangisinin ne yaptığını bildiğimde yeterince açık olmadığını itiraf ediyorum).
Piotr Lesnicki

1
Jay, cevabını benimkine göre tamamlarsan ('w' yi tamamlamak için) benimkini kaldırabilirim.
Piotr Lesnicki

2
Bazı işletim sistemleri için, yürütülebilir dosyanın uzantısını eklemeniz gerekebilir. Örneğin, Ubuntu üzerinde hangi ("scp") yazabilirim ama Windows'da, hangi ("scp.exe") yazmak gerekiyordu.
waffleman

13
"Os.path.exists" i "os.path.isfile" olarak değiştirmenizi öneririm. Aksi takdirde, Unix'te bu, bir dizini + x bit ayarıyla yanlış eşleştirebilir. Ben de bu işlevin üstüne eklemek yararlı bulabilirsiniz: import sys; sys.platform == "win32" ise ve program.endswith (". exe") değil: program + = ".exe". Bu şekilde, bir cmd penceresinde olduğu gibi, "calc" veya "calc.exe" ye başvurabilirsiniz.
Kevin Ivarsen

1
@KevinIvarsen Daha iyi bir seçenek değerleri döngü olacaktır PATHEXTçünkü env var commandgeçerli olduğu command.comgibi scriptvsscript.bat
Lekensteyn

325

Bunun eski bir soru olduğunu biliyorum, ama kullanabilirsiniz distutils.spawn.find_executable. Bu, python 2.4'ten beri belgelenmiştir ve python 1.6'dan beri mevcuttur.

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

Ayrıca, Python 3.3 şimdi sunuyor shutil.which().


7
Tarihinde win32, distutils.spawn.find_executableuygulama .exe, kümesinde aramak için uzantı listesini kullanmak yerine yalnızca arar %PATHEXT%. Bu harika değil, ama birisinin ihtiyaç duyduğu tüm durumlar için işe yarayabilir.
rakslice

7
örnek kullanım:from distutils import spawn php_path = spawn.find_executable("php")
codefreak

6
Görünüşe göre distutils.spawngüvenilir bir şekilde mevcut değil: OS X 10.10'da Python 2.7.6'daki Sistem kurulumum (/ usr / bin / python) ile şunu alıyorum: AttributeError: 'module' object has no attribute 'spawn'garip bir şekilde aynı Python sürümü ile aynı makinede çalışıyor, ancak bir sanal kurulum.
Josh Kupershmidt

8
@JoshKupershmidt, sözdiziminden çok emin olun import distutils.spawnveya from distutils import spawnsözdizimini takip edin import distutils. Aksi takdirde erişilemeyebilir ve AttributeErrororada olsa bile yukarıdakileri alacaksınız .
John St.John


39

Python 3.2 ve öncesi için:

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

Bu Jay'in cevabının bir astarı , ayrıca burada bir lambda fonk olarak:

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

Veya son olarak, bir işlev olarak girintili:

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 ve üstü için:

import shutil

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

Jan-Philip Gehrcke'nin tek çizgisi olarak Cevap :

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

Bir def olarak:

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

1
"bir işlev olarak girintili" sürümü xolması gereken değişkeni kullanırcmd
0x89 13:15

ayrıca os.path.join(path, cmd)bir dosya olup olmadığını görmek için bir test eklemeniz gerekir , değil mi? Afterall, dizinler de çalıştırılabilir biti ayarlanmış olabilir ...
MestreLion

@MestreLion Bu olası bir durum gibi görünüyor, bu davranışı onaylamak ve bu yanıtı güncellemek ister misiniz? Yardımcı olursa, bu yayını bir topluluk wiki'sine değiştirmekten mutluyum.
ThorSummoner

1
@ThorSummoner: Onayladım ve gerçekten dosya için test gerektiriyor. Basit bir test: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(...)Bunu düzeltmek için uygun yerlere basit bir şey eklemek yeterli
MestreLion

19

Windows'ta dosya uzantısını belirtmeyi unutmayın. Aksi takdirde, ortam değişkenini is_exekullanarak pencereler için çok karmaşık yazmanız gerekir PATHEXT. Sadece FindPath'i kullanmak isteyebilirsiniz .

OTOH, çalıştırılabilir dosyayı aramak için neden uğraşıyorsun? İşletim sistemi, popençağrının bir parçası olarak bunu sizin için yapar ve yürütülebilir dosya bulunamazsa bir istisna oluşturur. Tek yapmanız gereken verilen işletim sistemi için doğru istisnayı yakalamak. Windows'da bulunmazsa subprocess.Popen(exe, shell=True)sessizce başarısız olacağını unutmayın exe.


Birleştirilerek PATHEXTyukarıdaki uygulamaya which(Jay'in yanıtında):

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
Kabul edilen cevaptaki bir hatayı düzeltti, bunun yerine bu cevabın üstte olması gerektiğini hissediyorum.
NiTe Luo

akıllı kullanımı yieldiçinde ext_candidates, bana nasıl anahtar kelime eserlerin daha iyi anlaşılmasını verdi
Grant Humphries

15

* Nix platformları için (Linux ve OS X)

Bu benim için çalışıyor gibi görünüyor:

Mestreion sayesinde Linux üzerinde çalışmak üzere düzenlendi

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

Burada yaptığımız şey yerleşik komutu kullanmak typeve çıkış kodunu kontrol etmektir. Böyle bir komut yoksa, type1'den (veya yine de sıfır olmayan bir durum kodundan) çıkar.

Stdout ve stderr ile ilgili bit sadece typekomutun çıktısını susturmaktır , çünkü sadece çıkış durum kodu ile ilgileniyoruz.

Örnek kullanım:

>>> 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
Bunun işe yaradığından emin misin ? Bu çok hoş bir yaklaşım, ama typebir kabuk yerleşik, yürütülebilir bir dosya değil, bu yüzden subprocess.call()burada başarısız.
MestreLion

1
Denediniz mi yoksa sadece teori mi hazırlıyorsunuz? Yine de benim mac üzerinde çalışır.
hasen

Ubuntu 12.04'te denedim, atıyor OSError: [Errno 2] No such file or directory. Belki Mac'te typegerçek bir komuttur
MestreLion

2
Bir sonra LOT eklenti: test, nasıl düzeltme saptadığımız shell=Trueve değiştirme ["type", cmd]için"type " + cmd
MestreLion

4
Dikkat: "cmd" değişkeninin geçerli veriler içerdiğinden emin olun. Harici bir kaynaktan geliyorsa, kötü bir adam size "ls; rm -rf /" verebilir. Bence python çözümü (alt süreç olmadan) çok daha iyi. Sonraki nokta: Bu yöntemi sık sık çağırırsanız, birçok işlemin ortaya çıkması gerektiğinden alt işlem çözümü çok daha yavaştır.
guettli

7

Yol adlarıyla ilgili bazı yararlı işlevler için os.path modülüne bakın . Varolan bir dosyanın yürütülebilir olup olmadığını kontrol etmek için os.X_OK moduyla os.access (yol, mod) kullanın .

os.X_OK

Yolun yürütülüp yürütülemeyeceğini belirlemek için access () mod parametresine dahil edilecek değer.

DÜZENLEME: Önerilen which()uygulamalarda os.path.join()tam dosya adları oluşturmak için bir ipucu eksik .


Teşekkürler gimel, bu yüzden temelde cevabım var: böyle bir fonksiyon yok, bunu manuel olarak yapmalıyım.
Piotr Lesnicki

Os.access kullanmayın. erişim işlevi, özel programlar için tasarlanmıştır.
Changming Sun

6

Bağışlama izni daha kolay istemek temelinde ben sadece onu kullanmak ve hatayı yakalamak denemek istiyorum (bu durumda OSError - Ben dosya yok ve dosya çalıştırılamaz ve her ikisi de OSError verir).

Yürütülebilir dosya --versionhızlı bir işlem olmayan bir bayrak gibi bir şey varsa yardımcı olur .

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

Bu genel bir çözüm değildir, ancak birçok kullanım durumu için en kolay yol olacaktır - kodun iyi bilinen tek bir yürütülebilir dosyayı araması gereken durumlar.


3
--versionAdlı bir programı çağırmak bile çok tehlikeli launchmissiles!
xApple

1
+1, bu yaklaşımı seviyorum. EAFP altın bir Python kuralıdır. Belki de UI kurmak dışında, launchmissiesfüzeleri başlatmak istemiyorsanız neden var olup olmadığını bilmek istersiniz?
Yürütmek

Bu yöntemin sorunu, çıktının konsola yazdırılmasıdır. Boru ve kabuk = Doğru kullanırsanız, OSError asla yükselmez
Nick Humrich

MacOS'ta, örneğin gitkörü körüne çalıştırmak istemediğiniz için saplama yürütülebilir dosyaları da vardır .
Bob Aman

5

Burada biraz büyücü olduğumu biliyorum, ama bu soruya rastladım ve kabul edilen çözüm tüm durumlar için işe yaramadı Yine de göndermenin yararlı olabileceğini düşündüm. Özellikle, "yürütülebilir" mod algılama ve dosya uzantısının sağlanması gereksinimi. Ayrıca, hem python3.3 'ler shutil.which(kullanımları PATHEXT) hem de python2.4 +' lar distutils.spawn.find_executable(sadece eklemeye çalışır '.exe') yalnızca vakaların bir alt kümesinde çalışır .

Bu yüzden "süper" bir versiyon yazdım (kabul edilen cevap ve PATHEXTSuraj'ın önerisine dayanarak ). Bu sürümü which, görevi biraz daha ayrıntılı bir şekilde yapar ve önce bir dizi "geniş faz" önce genişlik tekniklerini dener ve sonunda PATHalan üzerinde daha ayrıntılı aramaları dener :

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

Kullanım şöyle görünür:

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

Kabul edilen çözüm dosyaları gibi vardı çünkü, bu durumda benim için iş vermedi meld.1, meld.ico, meld.doapvb da kabul yanıtında çalıştırılabilir testi eksik ve verme çünkü (sözlük sırasında ilk beri muhtemelen) yerine iade edildi, bunlardan biri dizininde yanlış pozitifler.




2

Bu yeterince basit görünüyor ve hem python 2 hem de 3'te çalışıyor

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

Maalesef Jaap, ancak bu çözüm yalnızca yürütülebilir dosya yanlış çağrıldığında bir çıkış kodu 1'i çağırmazsa çalışır. Yani, örneğin, "dir" ve "ls" için çalışacaktır, ancak yapılandırma gerektiren bir şeye karşı yürütürseniz, yürütülebilir olsa bile kırılır.
Spedge

1
Tam olarak "yapılandırma gerektir" ile ne demek istiyorsun? Tek başına 'hangi' aslında hiçbir şey yürütmez, sadece PATH'i bu adla yürütülebilir bir varlığın olup olmadığını kontrol eder (ki bu adam).
jaap

1
Ohh, bu yüzden çalıştırılabilir dosyayı bulmak için "hangisini" kullanıyorsunuz. Yani bu sadece Linux / Unix için mi çalışıyor?
Spedge

1
Evrensel olmak için command -v executableveya type executabledüğmesini kullanın . Mac'lerde beklenen sonuçları döndürmeyen durumlar vardır.
RJ

1

Önemli bir soru " Yürütülebilir dosya olup olmadığını neden test etmeniz gerekiyor?" Belki de bilmiyorsun? ;-)

Son zamanlarda PNG dosyası için görüntüleyiciyi başlatmak için bu işlevselliğe ihtiyacım vardı. Önceden tanımlanmış bazı izleyicileri yinelemek ve var olan ilkini çalıştırmak istedim. Neyse ki karşılaştım os.startfile. Bu daha iyi! Basit, taşınabilir ve sistemdeki varsayılan görüntüleyiciyi kullanır :

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

Güncelleme:os.startfile Taşınabilir olma konusunda yanılmışım ... Sadece Windows. Mac'te openkomutu çalıştırmanız gerekir . Ve xdg_openUnix'te. Mac ve Unix desteği eklerken bir Python sorunu var os.startfile.



1

Windows desteği eklendi

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 modülünde bir dosya olup olmadığını anlayabilirsiniz. Özellikle bir yürütülebilir dosya nix üzerinde windows üzerinde olmayan ve tersi çok şey göz önüne alındığında oldukça mantıklı görünmüyor.


0

Belli bir seçim "hangisi" gibi görünüyor, sonuçları popen ile ayrıştırıyor, ancak os sınıfını kullanarak bunu simüle edebilirsiniz. Pseudopython'da şöyle görünecektir:

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

Os.exec veya bunun gibi bir şey kullanarak bir "hangi" komutunu çalıştırma konusunda dikkatli olurdum. Yalnızca yavaş değil (performans herhangi bir endişe kaynağıysa), aynı zamanda exec değişkeninizin bir parçası olarak bir değişken kullanıyorsanız, güvenlik bir endişe haline gelir. Birisi bir "rm -rf /" içinde gizlice girebilir.
Parappa

1
Hangi, os.popen işlevini kullanarak program tarafından oluşturulan bir komutu çalıştırmak için kullanacağımızdan, aslında geçerli değil, değil mi?
Charlie Martin

2
Teşekkürler, ancak pencerelerde ve benzerlerinde 'hangisinin' var olup olmadığından emin değilim. Ben aslında standart lib fantezi bir şey olup olmadığını bilmek istedim
Piotr Lesnicki

Standart Windows kurulumlarında hala whichkomut yoktur ; UnxUtils sürümü vardır, ancak uzantıyı bilmeniz / belirtmeniz gerekir, aksi takdirde program bulunmaz.
Tobias

0

Temel olarak, bağlı dosya sistemindeki bir dosyayı (yalnızca PATH dizinlerinde olması gerekmez) bulmak ve yürütülebilir olup olmadığını kontrol etmek istersiniz. Bu, aşağıdaki plan anlamına gelir:

  • yerel olarak monte edilmiş dosya sistemlerindeki tüm dosyaları numaralandırma
  • sonuçları isim modeliyle eşleştir
  • bulunan her dosya için yürütülebilir olup olmadığını kontrol edin

Bunu taşınabilir bir şekilde yapmak, çok fazla bilgi işlem gücü ve zaman gerektireceğini söyleyebilirim. Gerçekten ihtiyacınız olan şey mi?


0

Bir yoktur which.py standart Python dağılımındaki komut (örneğin Windows üzerinde '\PythonXX\Tools\Scripts\which.py').

EDIT: which.pybağlıdır, lsbu nedenle çapraz platform değildir.


0

Önceki örneklerin hiçbiri tüm platformlarda çalışmaz. Genellikle dosya uzantısı olmadan yürütebileceğiniz ve yeni uzantı kaydedebileceğiniz için Windows'ta çalışmazlar . Örneğin Windows'da python iyi yüklenmişse, 'file.py' dosyasını çalıştırmak yeterlidir ve çalışır.

Sahip olduğum tek geçerli ve taşınabilir çözüm, komutu yürütmek ve hata kodunu görmekti. İyi bir yürütülebilir dosya, hiçbir şey yapmayacak bir dizi arama parametresine sahip olmalıdır.


-3

Python kumaş kitaplığını kullanma:

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
Bu çok kötü bir öneri. Kelimenin tam anlamıyla programı (Python stdlib'in kolayca yapabileceği) yerel bir program oluşturmak için uzaktan yürütme kitaplığına bağımlı hale getiriyorsunuz ve ayrıca tüm sistemlerde hangisinin bulunmadığına bağlısınız. which(1)
Michał Górny
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.