txt - ¿Probar si el ejecutable existe en Python?
manejo de archivos y carpetas en python (22)
Para plataformas * nix (Linux y OS X)
Esto parece estar funcionando para mí:
Editado para trabajar en Linux, gracias a MestreLion
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
Lo que estamos haciendo aquí es usar el type
comando incorporado y verificar el código de salida. Si no hay tal comando, el type
saldrá con 1 (o un código de estado distinto de cero).
El bit sobre stdout y stderr es solo para silenciar la salida del comando type
, ya que solo estamos interesados en el código de estado de salida.
Ejemplo de uso:
def cmd_exists(cmd):
return subprocess.call("type " + cmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
En Python, ¿existe una forma portátil y sencilla de probar si existe un programa ejecutable?
Por simple quiero decir algo como el comando que sería simplemente perfecto. No quiero buscar PATH manualmente o algo que involucre tratar de ejecutarlo con Popen
& al y ver si falla (eso es lo que estoy haciendo ahora, pero imagino que es launchmissiles
de launchmissiles
)
Para Python 3.2 y anteriores:
my_command = ''ls''
any(os.access(os.path.join(path, my_command), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))
Esta es una frase de Jay''s Answer , también aquí como una función lambda:
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'')
O por último, con sangría como función:
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)
)
Para python 3.3 y posteriores:
import shutil
command = ''ls''
shutil.which(command) is not None
Como respuesta de Jan-Philip Gehrcke :
cmd_exists = lambda x: shutil.which(x) is not None
Como una definición:
def cmd_exists(cmd):
return shutil.which(cmd) is not None
Añadido soporte de ventanas
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
Básicamente, desea buscar un archivo en el sistema de archivos montado (no necesariamente en los directorios PATH) y verificar si es ejecutable. Esto se traduce en el siguiente plan:
- enumerar todos los archivos en sistemas de archivos montados localmente
- emparejar resultados con nombre patrón
- para cada archivo encontrado comprobar si es ejecutable
Yo diría que hacer esto de manera portátil requerirá mucha potencia de computación y tiempo. ¿Es realmente lo que necesitas?
El mejor ejemplo debería ser el módulo de python bulit-in shutil.which () en Python 3. El enlace es https://hg.python.org/cpython/file/default/Lib/shutil.py
Encontré algo en que me solucionó el problema. Esto funciona siempre que el ejecutable tenga una opción (como --help o --version) que genere algo y devuelva un estado de salida de cero. Consulte Suprimir la salida en las llamadas de Python a los ejecutables : el "resultado" al final del fragmento de código en esta respuesta será cero si el ejecutable está en la ruta, de lo contrario es más probable que sea 1.
Esto parece bastante simple y funciona tanto en python 2 como en 3.
try: subprocess.check_output(''which executable'',shell=True)
except: sys.exit(''ERROR: executable not found'')
La forma más fácil en la que puedo pensar:
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
Edición : Ejemplo de código actualizado para incluir lógica para el caso de manejo donde el argumento proporcionado ya es una ruta completa al ejecutable, es decir, "which / bin / ls". Esto imita el comportamiento del comando ''which'' de UNIX.
Edición : Actualizado para usar os.path.isfile () en lugar de os.path.exists () por comentarios.
Edit : path.strip(''"'')
parece ser lo que hay que hacer aquí. Parece que ni Windows ni POSIX fomentan los elementos PATH citados.
Ninguno de los ejemplos anteriores funciona en todas las plataformas. Por lo general, no funcionan en Windows porque puede ejecutar sin la extensión de archivo y puede registrar una nueva extensión. Por ejemplo, en Windows, si Python está bien instalado, es suficiente para ejecutar ''file.py'' y funcionará.
La única solución válida y portátil que tuve fue ejecutar el comando y ver el código de error. Cualquier ejecutable decente debe tener un conjunto de parámetros de llamada que no harán nada.
Parecería que la elección obvia es "cuál", analizar los resultados a través de popen, pero podría simularlo de otra manera utilizando la clase os. En pseudopython, se vería así:
for each element r in path:
for each file f in directory p:
if f is executable:
return True
Puede probar la biblioteca externa llamada "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
Python 3.3 ahora ofrece shutil.which() .
Sé que esta es una pregunta antigua, pero puedes usar distutils.spawn.find_executable
. Esto se ha documentado desde Python 2.4 y ha existido desde Python 1.6.
import distutils.spawn
distutils.spawn.find_executable("notepad.exe")
Además, Python 3.3 ahora ofrece shutil.which()
.
Sé que estoy siendo un poco nigromante aquí, pero me topé con esta pregunta y la solución aceptada no funcionó para mí en todos los casos. Pensé que podría ser útil presentarla de todos modos. En particular, la detección del modo "ejecutable" y el requisito de suministrar la extensión de archivo. Además, tanto shutil.which de shutil.which
(usa PATHEXT
) como distutils.spawn.find_executable de python2.4 + (solo intentan agregar ''.exe''
) solo funcionan en un subconjunto de casos.
Así que escribí una versión "súper" (basada en la respuesta aceptada y la sugerencia PATHEXT
de Suraj). Esta versión de la which
realiza la tarea un poco más exhaustivamente, e intenta primero una serie de técnicas de "amplitud de banda", y finalmente intenta búsquedas más detalladas en el espacio 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
El uso se ve así:
>>> which.which("meld")
''C://Program Files (x86)//Meld//meld//meld.exe''
La solución aceptada no funcionó para mí en este caso, ya que había archivos como meld.1
, meld.ico
, meld.doap
, etc. también en el directorio, uno de los cuales se devolvió en su lugar (probablemente desde el principio lexicográficamente) porque el ejecutable La prueba en la respuesta aceptada fue incompleta y dio falsos positivos.
Si tiene bash
y una función sh
( subprocess.Popen( ... ).communicate()
),
utilizar el type
basin builtin:
type -p ls => /bin/ls
type -p nonesuch => ""
Sobre la base de que es más fácil pedir perdón que permiso , solo trataría de usarlo y detectar el error (OSError en este caso, verifiqué si el archivo no existe y el archivo no es ejecutable y ambos dan OSError).
Ayuda si el ejecutable tiene algo así como una --version
que es una no-op rápida.
import subprocess
myexec = "python2.8"
try:
subprocess.call([myexec, ''--version'']
except OSError:
print "%s not found on path" % myexec
Esta no es una solución general, pero será la forma más fácil para muchos casos de uso, aquellos en los que el código debe buscar un único ejecutable conocido.
Solo recuerda especificar la extensión de archivo en windows. De lo contrario, tiene que escribir un is_exe
mucho más complicado para Windows usando la variable de entorno PATHEXT
. Es posible que desee utilizar FindPath .
OTOH, ¿por qué te molestas en buscar el ejecutable? El sistema operativo lo hará por usted como parte de la llamada popen
y generará una excepción si no se encuentra el ejecutable. Todo lo que necesita hacer es capturar la excepción correcta para el sistema operativo dado. Tenga en cuenta que en Windows, subprocess.Popen(exe, shell=True)
fallará silenciosamente si no se encuentra exe
.
Incorporando PATHEXT
en la implementación anterior de la which
(en la respuesta de Jay):
>>> cmd_exists("jsmin")
True
>>> cmd_exists("cssmin")
False
>>> cmd_exists("ls")
True
>>> cmd_exists("dir")
False
>>> cmd_exists("node")
True
>>> cmd_exists("steam")
False
Una pregunta importante es " ¿Por qué necesitas probar si existe un ejecutable?" Tal vez usted no? ;-)
Hace poco necesité esta funcionalidad para iniciar el visor para el archivo PNG. Quería recorrer algunos visores predefinidos y ejecutar el primero que existe. Afortunadamente, me encontré con os.startfile
. ¡Es mucho mejor! Simple, portátil y utiliza el visor predeterminado en el sistema:
>>> os.startfile(''yourfile.png'')
Actualización: Me equivoqué sobre que os.startfile
sea portátil ... Es solo para Windows. En Mac tienes que ejecutar open
comando open
. Y xdg_open
en Unix. Hay un problema de Python al agregar soporte para Mac y Unix para os.startfile
.
Usando la biblioteca de telas de 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
Vea el módulo os.path para algunas funciones útiles en las rutas de acceso. Para verificar si un archivo existente es ejecutable, use os.access (ruta, modo) , con el modo os.X_OK.
os.X_OK
Valor que se incluirá en el parámetro de modo de acceso () para determinar si se puede ejecutar la ruta.
EDITAR: Las implementaciones de las which()
sugieren una pista: el uso de os.path.join()
para crear nombres de archivo completos.
puedes saber si un archivo existe con el módulo os. un ejecutable en particular parece bastante inportable considerando que muchas cosas son ejecutables en nix que no están en Windows y viceversa.