2012-06-26 19 views
25

Jak sprawdzić, czy program istnieje ze skryptu Pythona?Sprawdź, czy program istnieje ze skryptu pythonowego

Załóżmy, że chcesz sprawdzić, czy są dostępne wget lub curl. Zakładamy, że powinni znaleźć się na ścieżce.

Najlepiej byłoby zobaczyć rozwiązanie wieloplatformowe, ale na razie Linux wystarczy.

wskazówki:

  • uruchamiając komendę i sprawdzanie kodu powrotu nie zawsze wystarcza, ponieważ niektóre narzędzia zrobić powrócić non 0 wynik nawet przy próbie --version.
  • nic nie powinno być widoczne na ekranie podczas sprawdzania komendy

Również byłbym wdzięczny rozwiązanie, które jest bardziej ogólne, jak is_tool(name)

+0

możliwe duplikat [Test jeśli wykonywalny istnieje w języku Python?] (http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python) –

Odpowiedz

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

To spowodowałoby, że podproces uruchomiłby się na czas nieokreślony, jeśli wypełniłby bufory potoków standardowego wyjścia lub stderr. Jeśli chcesz uruchomić proces * w ogóle * tylko w celu sprawdzenia, czy istnieje, powinieneś użyć otwartego 'os.devnull' i użyć go jako stdout i stderr. –

+0

mało prawdopodobne, ale masz rację, dziękuję. – sorin

+0

Wiele informacji na temat użycia narzędzi wyjściowych po wywołaniu bez parametrów, które mogłyby z łatwością wypełnić bufory rur. W każdym razie myliłem się z moim wstępnym komentarzem - brakowało mi połączenia z "comunnicate()", które znajdowało się poza prawym marginesem skrzynki z kodami i nie przewijałem wystarczająco daleko w prawo. Metoda 'Popen.communicate()' dba o uniknięcie wszelkich zakleszczeń. –

3

Prawdopodobnie bulić do which wget lub which curl i sprawdzenie wynik kończy się na nazwie programu, którego używasz. Magia unixa :)

Właściwie wszystko, co musisz zrobić, to sprawdzić kod powrotny which. Więc ... za pomocą naszego zaufanego subprocess moduł:

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

pamiętać, że sprawdzili to na oknach z Cygwin ... Jeśli chcesz dowiedzieć się, jak zaimplementować which w czystym Pythonie, proponuję sprawdzić tutaj: http://pypi.python.org/pypi/pycoreutils (ojej - wydaje się, że nie dostarczają which Czas przyjaznej sugerować.?)

UPDATE: w systemie Windows można użyć where zamiast which o podobnym działaniu.

+1

Czy nie próbowałoby to uruchomić programu o nazwie '" which wget "', tzn. z spacja w nazwie pliku? –

+0

@SvenMarnach, racja! Mam składnię wszystko źle :(oh kochanie –

+1

Powinno to być: 'subprocess.call (['co', 'wget'])' – nbubis

31

Najprostszym sposobem jest próba uruchomienia programu o pożądanych parametrach, i obsłużyć wyjątek, jeśli nie istnieją:

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 

Jest to wspólny wzór w Pythonie: EAFP

+0

prawo dla programów, z których wiesz, jak korzystać, jest to prawdopodobnie najlepszy sposób na zrobienie tego, ponieważ: oczekiwanie sukcesu często prowadzi do czystszego kodu ... –

+1

@DarenThomas: Dla programów nie wiem jak używać, informacja, czy istnieją, czy nie, nie wydaje się zbyt przydatna. :) –

+0

Podobają mi się twoje podejście, ale zanieczyszczają standardowe wyjście i stderr. Ponadto nie jest funkcją;) – 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

To nie jest niezależne od platformy: jeśli naprawdę chcesz powielić funkcjonalność systemu operacyjnego, powinieneś przynajmniej użyć 'os.path.join()' i 'os.pathsep'. –

+0

(Edytowałem również w 'os.pathsep' - w systemie Windows,' PATH' jest rozdzielany średnikami.) –

+0

dobry połów, czy moja edycja zastąpiła twój pathsep? – ryanday

14

można użyć połączenia subprocess do binarnego potrzebne z:

  • "która": * nix
  • „gdzie” Win 2003 i później (XP posiada dodatek)

aby uzyskać wykonywalnego ścieżkę (zakładając, że jest na ścieżce środowiska).

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" 

lub po prostu użyj Ned Batchelder's wh.skrypt py, że jest to „które” cross wdrożenie platformy:

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

+0

, wywołanie nie generuje wyjątku, gdy program nie istnieje, zamiast tego wywołanie zwraca wartość niezerową – Photon

0

Niewielkie modyfikacje kodu użytkownika @ SvenMarnach że rozwiązuje problem drukowania do standardowego strumienia wyjściowego. Jeśli użyjesz funkcji subprocess.check_output() zamiast subprocess.call(), będziesz w stanie obsłużyć normalnie wydrukowany ciąg standardowego kodu i nadal przechwytywać wyjątki i kod statusu wyjścia.

Jeśli chcesz stłumić standardowy strumień wyjściowy w terminalu, nie wydrukować std się ciąg znaków, który jest zwracany z 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) 

niezerowy kod status wyjścia i ciąg wyjściowy są podnoszone w CalledProcessError jako subprocess.CalledProcessError.returncode i, dzięki czemu możesz robić, co chcesz z nimi.

Jeśli chcesz wydrukować standardowe wyjście wykonywalnego do terminala, drukowanie znaków, który jest zwracany:

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() dodaje dodatkowy przełamane na ciąg. Jeśli chcesz, aby wyeliminować że (i pisać std błędu do strumienia std err zamiast std zewnątrz strumienia jak pokazano z print() oświadczenia powyżej), należy sys.stdout.write(string) i sys.stderr.write(string) zamiast print():

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

Dla systemów opartych na Debianie:

Testowałem skrypty z góry i nie były one naprawdę dobre. Prowadzą programy i to jest denerwujące, ponieważ zajmuje dużo czasu i trzeba zamknąć programy. Znalazłem rozwiązanie pobierające zainstalowane pakiety za pomocą aptitude, a następnie czytając listę.

Można stosować różne rodzaje poleceń, aby uzyskać różne rodzaje „zainstalowanych pakietów” Exemples: https://askubuntu.com/questions/17823/how-to-list-all-installed-packages

dwa, że ​​uważam, że najlepiej było:

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

Można je uruchomić w terminal, aby je przetestować.


Funkcja 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

Działa to tylko w systemach opartych na Debianie i nie powiedzie się źle, gdy nazwy pakietów są zmienione, co czasami się zdarza. –

10

pójdę do:

import distutils.spawn 

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

'distutils.spawn' działa dobrze w systemach Linux i Mac OS X. Ale w drugim przypadku, jeśli tworzysz' app', i klikniesz dwukrotnie, aby wykonać, 'distutils.spawn' zawsze zwraca' None'. – muammar

23

shutil.which

Niech mi polecić opcję, która nie została jeszcze omówione: implementacja Pythona which, konkretnie shutil.which. Został wprowadzony w Pythonie 3.3 i jest wieloplatformowy, obsługujący systemy Linux, Mac i Windows. Jest również dostępny w Pythonie 2.x przez whichcraft. Możesz także po prostu zgrać kod na which zaraz po czym here i wstawić go do swojego programu.

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

Inną opcją, która już wspomniano jest distutils.spawn.find_executable.

find_executable „s docstring jest następujący:

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

Więc jeśli zwrócić uwagę, można zauważyć, że nazwa funkcji jest nieco mylący. W przeciwieństwie do which, find_executable faktycznie nie sprawdza, czy executable jest oznaczony jako wykonywalny, tylko że znajduje się na ścieżce PATH. Jest więc całkowicie możliwe (jednak mało prawdopodobne), że find_executable wskazuje, że program jest dostępny, gdy tak nie jest.

Załóżmy na przykład, że masz plik /usr/bin/wget, który nie jest oznaczony jako plik wykonywalny. Uruchomienie wget z powłoki spowoduje następujący błąd: bash:/usr/bin/wget: Odmowa dostępu. which('wget') is not None zwróci False, ale find_executable('wget') is not None zwróci wartość True. Prawdopodobnie można uciec z wykorzystaniem obu funkcji, ale jest to po prostu coś, o czym należy pamiętać przy pomocy 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

Link do dokumentów dla 'shutil.which()': https://docs.python.org/3/library/shutil.html#shutil.which –

+0

do głosowania na distutils.spawn.find_executable! Dzięki! – Barmaley

2

bym zmienić @ odpowiedź Sorin w następujący sposób, powodem jest to sprawdzić nazwę programu bez przechodzenia bezwzględną ścieżkę programu

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

Niestety, to rozwiązanie nie działa na platformie Windows –