2012-06-26 2 views
25

Comment vérifier si un programme existe à partir d'un script python? Supposons que vous vouliez vérifier si wget ou curl sont disponibles. Nous supposerons qu'ils devraient être dans le chemin.Vérifier si un programme existe à partir d'un script python

Ce serait le meilleur pour voir une solution multiplateforme mais pour le moment, Linux est suffisant.

Conseils:

  • exécutant la commande et le contrôle du code de retour ne sont pas toujours assez que certains outils ne retourner pas 0 résultat même lorsque vous essayez --version.
  • rien ne doit être visible à l'écran lors de la vérification de la commande

Aussi, je vous serais reconnaissant une solution qui est plus générale, comme is_tool(name)

+0

double possible de [Test si exécutable existe en Python?] (http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python) –

Répondre

8
import subprocess 
import os 

def is_tool(name): 
    try: 
     devnull = open(os.devnull) 
     subprocess.Popen([name], stdout=devnull, stderr=devnull).communicate() 
    except OSError as e: 
     if e.errno == os.errno.ENOENT: 
      return False 
    return True 
+2

Cela laisserait le sous-processus s'exécuter indéfiniment s'il remplit les tampons de canal de stdout ou de stderr. Si vous voulez exécuter le processus * du tout * juste pour vérifier s'il existe, vous devez utiliser 'os.devnull' et l'utiliser comme stdout et stderr. –

+0

pas susceptible de se produire, mais vous avez raison, merci. – sorin

+0

De nombreux outils affichent des informations d'utilisation lorsqu'ils sont appelés sans paramètres, ce qui peut facilement remplir les tampons du tube. Je me trompais quand même avec mon commentaire initial - j'ai manqué l'appel de 'comunnicate()', qui était au-delà de la marge droite de la boîte de code, et je n'ai pas fait défiler assez loin vers la droite. La méthode 'Popen.communicate()' prend soin d'éviter les blocages. –

3

Je shell probablement vers which wget ou which curl et vérification que le résultat se termine par le nom du programme que vous utilisez. La magie d'unix :)

En fait, tout ce que vous devez faire est de vérifier le code retour de which. Alors ... en utilisant notre module subprocess fidèle:

import 
rc = subprocess.call(['which', 'wget']) 
if rc == 0: 
    print 'wget installed!' 
else: 
    print 'wget missing in path!' 

Notez que je l'ai testé ceci sur Windows avec Cygwin ... Si vous voulez savoir comment mettre en œuvre which en python pur, je vous suggère de vérifier ici: http://pypi.python.org/pypi/pycoreutils (oh cher - il semble qu'ils ne fournissent pas which Le temps d'un coup de coude amical.?)

MISE à JOUR: Sous Windows, vous pouvez utiliser where au lieu de which pour un effet similaire.

+1

N'essayerions-nous pas d'exécuter un programme nommé '" which wget "', ie avec un espace dans le nom de fichier? –

+0

@SvenMarnach, à droite! J'ai la syntaxe tout faux :(oh cher. –

+1

Cela devrait être: 'subprocess.call (['qui', 'wget'])' – nbubis

31

La façon la plus simple est d'essayer d'exécuter le programme avec les paramètres souhaités, et gérer l'exception si elle n'existe pas:

try: 
    subprocess.call(["wget", "your", "parameters", "here"]) 
except OSError as e: 
    if e.errno == os.errno.ENOENT: 
     # handle file not found error. 
    else: 
     # Something else went wrong while trying to run `wget` 
     raise 

Ceci est un modèle commun en Python: EAFP

+0

droite.pour les programmes que vous savez utiliser, c'est probablement la meilleure façon de procéder.Après tout: attendre le succès conduit souvent à un code plus propre ... –

+1

@DarenThomas: Pour les programmes vous Je ne sais pas comment utiliser, l'information si elles existent ou non ne semble pas être trop utile. :) –

+0

J'aime votre approche, mais elle pollue stdout et stderr. En outre, ce n'est pas une fonction;) – sorin

1
import os 
import subprocess 


def is_tool(prog): 
    for dir in os.environ['PATH'].split(os.pathsep): 
     if os.path.exists(os.path.join(dir, prog)): 
      try: 
       subprocess.call([os.path.join(dir, prog)], 
           stdout=subprocess.PIPE, 
           stderr=subprocess.STDOUT) 
      except OSError, e: 
       return False 
      return True 
    return False 
+0

Ceci n'est pas indépendant de la plate-forme: Si vous voulez vraiment répliquer la fonctionnalité du système d'exploitation, vous devez au moins utiliser os.path.join() 'et' os.pathsep'. –

+0

(J'ai également édité dans 'os.pathsep' - sur Windows,' PATH' est délimité par des points-virgules.) –

+0

bonne capture, est-ce que ma modification a écrasé votre pathsep? – ryanday

14

Vous pouvez utiliser un appel au sous-processus binaire nécessaire avec:

  • "qui": * nix
  • "où": Win 2003 et plus tard (Xp a un addon)

pour obtenir le chemin exécutable (en supposant qu'il est dans le chemin de l'environnement).

import os 
import platform 
import subprocess 

cmd = "where" if platform.system() == "Windows" else "which" 
try: 
    subprocess.call([cmd, your_executable_to_check_here]) 
except: 
    print "No executable" 

ou simplement d'utiliser le tampon de Ned Batchelder.script py, qui est un "qui" la mise en œuvre de la plate-forme croisée:

http://nedbatchelder.com/code/utilities/wh_py.html

+0

L'appel ne lance pas d'exception lorsque le programme n'existe pas, l'appel renvoie une valeur non nulle. – Photon

0

Une légère modification au code de @ SvenMarnach qui aborde la question de l'impression dans le flux de sortie standard. Si vous utilisez la fonction subprocess.check_output() au lieu de subprocess.call(), vous pouvez gérer la chaîne normalement imprimée dans votre code, tout en interceptant les exceptions et le code d'état de sortie.

Si vous voulez supprimer le flux de sortie standard dans le terminal, ne pas imprimer la std sur la chaîne qui est renvoyée par check_output:

import subprocess 
import os 
try: 
    stdout_string = subprocess.check_output(["wget", "--help"], stderr=subprocess.STDOUT) 
    # print(stdout_string) 
except subprocess.CalledProcessError as cpe: 
    print(cpe.returncode) 
    print(cpe.output) 
except OSError as e: 
    if e.errno == os.errno.ENOENT: 
     print(e) 
    else: 
     # Something else went wrong while trying to run `wget` 
     print(e) 

Le code d'état de sortie non nulle et chaîne de sortie sont élevés dans le CalledProcessError comme subprocess.CalledProcessError.returncode et subprocess.CalledProcessError.output afin que vous puissiez faire ce que vous voulez avec eux.

Si vous souhaitez imprimer sur la borne de sortie standard de l'exécutable, imprimez la chaîne renvoyée:

import subprocess 
import os 
try: 
    stdout_string = subprocess.check_output(["wget", "--help"], stderr=subprocess.STDOUT) 
    print(stdout_string) 
except subprocess.CalledProcessError as cpe: 
    print(cpe.returncode) 
    print(cpe.output) 
except OSError as e: 
    if e.errno == os.errno.ENOENT: 
     print(e) 
    else: 
     # Something else went wrong while trying to run `wget` 
     print(e) 

print() ajoute une nouvelle ligne supplémentaire à la chaîne. Si vous voulez éliminer que (erreurs d'écriture et std au flux err std au lieu de std en flux comme indiqué avec les états d'impression() ci-dessus), utilisez sys.stdout.write(string) et sys.stderr.write(string) au lieu d'impression():

import subprocess 
import os 
import sys 
try: 
    stdout_string = subprocess.check_output(["bogus"], stderr=subprocess.STDOUT) 
    sys.stdout.write(stdout_string) 
except subprocess.CalledProcessError as cpe: 
    sys.stderr.write(cpe.returncode) 
    sys.stderr.write(cpe.output) 
except OSError as e: 
    if e.errno == os.errno.ENOENT: 
     sys.stderr.write(e.strerror) 
    else: 
     # Something else went wrong while trying to run `wget` 
     sys.stderr.write(e.strerror) 
-1

Pour les systèmes basés sur Debian:

J'ai testé les scripts ci-dessus et ils n'étaient pas vraiment bons. Ils exécutent les programmes et c'est ennuyeux parce que cela prend beaucoup de temps et que vous devez fermer les programmes. J'ai trouvé une solution pour obtenir les paquets installés avec aptitude, puis j'ai lu la liste.

Vous pouvez utiliser différents types de commandes pour obtenir différents types de 'paquets installés de Exemples: https://askubuntu.com/questions/17823/how-to-list-all-installed-packages

Les deux que j'ai trouvé le meilleur était:

dpkg --get-selections   # big list 
aptitude search '~i!~M' -F  # great list 

Vous pouvez les exécuter dans la terminal pour les tester.


La fonction python:

import os,sys 

def check_for_program(program): 

    if not os.path.exists("/tmp/program_list"): 
     os.system("aptitude search '~i!~M' -F > /tmp/program_list") 

    with open('/tmp/program_list') as f: 
     for line in f: 
      if program in line: 
       return True 
    return False 
+2

Cela ne fonctionne que sur les systèmes basés sur Debian, et échouera gravement lorsque les paquets seront renommés, ce qui arrive parfois. –

10

Je vais pour:

import distutils.spawn 

def is_tool(name): 
    return distutils.spawn.find_executable(name) is not None 
+0

'distutils.spawn' fonctionne bien sous Linux et Mac OS X. Mais dans le dernier, si vous faites une' app', et que vous double-cliquez pour l'exécuter, 'distutils.spawn' retourne toujours' None'. – muammar

23

shutil.which

Laissez-moi vous recommander une option qui n'a pas encore été discuté: une implémentation Python de which, plus précisément shutil.which. Il a été introduit dans Python 3.3 et est multi-plateforme, supportant Linux, Mac et Windows. Il est également disponible en Python 2.x via whichcraft. Vous pouvez également extraire le code which directement à partir de here et l'insérer dans votre programme.

def is_tool(name): 
    """Check whether `name` is on PATH and marked as executable.""" 

    # from whichcraft import which 
    from shutil import which 

    return which(name) is not None 

distutils.spawn.find_executable

Une autre option qui a déjà été mentionné est distutils.spawn.find_executable.

de docstring de » find_executable est la suivante:

Tries to find 'executable' in the directories listed in 'path'

Donc, si vous faites attention, vous remarquerez que le nom de la fonction est quelque peu trompeur. Contrairement à which, find_executable ne vérifie pas réellement que executable est marqué comme exécutable, seulement qu'il se trouve sur le PATH. Il est donc tout à fait possible (même improbable) que find_executable indique qu'un programme est disponible quand ce n'est pas le cas. Par exemple, supposons que vous ayez un fichier /usr/bin/wget qui n'est pas marqué comme exécutable. Par exemple, supposons que vous avez un fichier /usr/bin/wget. L'exécution de à partir du shell entraînera l'erreur suivante: bash:/usr/bin/wget: Autorisation refusée. which('wget') is not None retournera False, mais find_executable('wget') is not None retournera True. Vous pouvez probablement vous en tirer avec l'une ou l'autre fonction, mais c'est juste quelque chose à savoir avec find_executable.

def is_tool(name): 
    """Check whether `name` is on PATH.""" 

    from distutils.spawn import find_executable 

    return find_executable(name) is not None 
+0

Lien vers les documents pour 'shutil.which()': https://docs.python.org/3/library/shutil.html#shutil.which –

+0

votez pour distutils.spawn.find_executable! Merci mec! – Barmaley

2

Je change @ réponse de Sorin comme suit, la raison est-il vérifiait le nom du programme sans passer le chemin absolu du programme

from subprocess import Popen, PIPE 

def check_program_exists(name): 
    p = Popen(['/usr/bin/which', name], stdout=PIPE, stderr=PIPE) 
    p.communicate() 
    return p.returncode == 0 
+0

Malheureusement, cette solution ne fonctionne pas pour la plate-forme Windows –

Questions connexes