2012-10-18 8 views
28

Bardzo konkretne pytanie (mam nadzieję): Jakie są różnice między następującymi trzema kodami?Podproces Pythona Popen.communicate() jest odpowiednikiem Popen.stdout.read()?

(I oczekiwać, że będzie tylko, że pierwszy nie czekać na proces potomny się skończył, natomiast drugie i trzecie z nich robić. Ale muszę być pewien, że to tylko różnica ...)

Cieszę się również inne uwagi/sugestie (choć jestem już sobie sprawę z niebezpieczeństw i ograniczeń shell=True cross-platform)

pamiętać, że już czytać Python subprocess interaction, why does my process work with Popen.communicate, but not Popen.stdout.read()? i że nie chcą/muszą współdziałać z programem po.

Należy również pamiętać, że już czytać Alternatives to Python Popen.communicate() memory limitations? ale ja naprawdę nie rozumiem ...

Wreszcie należy pamiętać, że zdaję sobie sprawę, że gdzieś tam jest ryzyko zakleszczenia, gdy jeden bufor jest wypełniona jednym wyjściem przy użyciu jednej metody, ale zgubiłem patrząc na jasnych wyjaśnień w Internecie ...

Pierwszy kod:

from subprocess import Popen, PIPE 

def exe_f(command='ls -l', shell=True): 
    """Function to execute a command and return stuff""" 

    process = Popen(command, shell=shell, stdout=PIPE, stderr=PIPE) 

    stdout = process.stdout.read() 
    stderr = process.stderr.read() 

    return process, stderr, stdout 

Drugi kod:

from subprocess import Popen, PIPE 
from subprocess import communicate 

def exe_f(command='ls -l', shell=True): 
    """Function to execute a command and return stuff""" 

    process = Popen(command, shell=shell, stdout=PIPE, stderr=PIPE) 

    (stdout, stderr) = process.communicate() 

    return process, stderr, stdout 

kod trzecie:

from subprocess import Popen, PIPE 
from subprocess import wait 

def exe_f(command='ls -l', shell=True): 
    """Function to execute a command and return stuff""" 

    process = Popen(command, shell=shell, stdout=PIPE, stderr=PIPE) 

    code = process.wait() 
    stdout = process.stdout.read() 
    stderr = process.stderr.read() 

    return process, stderr, stdout 

Dzięki.

Odpowiedz

37

Jeśli spojrzeć na źródło subprocess.communicate(), pokazuje doskonały przykład różnicy:

def communicate(self, input=None): 
    ... 
    # Optimization: If we are only using one pipe, or no pipe at 
    # all, using select() or threads is unnecessary. 
    if [self.stdin, self.stdout, self.stderr].count(None) >= 2: 
     stdout = None 
     stderr = None 
     if self.stdin: 
      if input: 
       self.stdin.write(input) 
      self.stdin.close() 
     elif self.stdout: 
      stdout = self.stdout.read() 
      self.stdout.close() 
     elif self.stderr: 
      stderr = self.stderr.read() 
      self.stderr.close() 
     self.wait() 
     return (stdout, stderr) 

    return self._communicate(input) 

Widać, że communicate czyni korzystanie z połączeń odczytu do stdout i stderr, a także wzywa wait() . To tylko kwestia porządku operacji. W twoim przypadku, ponieważ używasz PIPE zarówno stdout i stderr, to idzie do _communicate():

def _communicate(self, input): 
    stdout = None # Return 
    stderr = None # Return 

    if self.stdout: 
     stdout = [] 
     stdout_thread = threading.Thread(target=self._readerthread, 
             args=(self.stdout, stdout)) 
     stdout_thread.setDaemon(True) 
     stdout_thread.start() 
    if self.stderr: 
     stderr = [] 
     stderr_thread = threading.Thread(target=self._readerthread, 
             args=(self.stderr, stderr)) 
     stderr_thread.setDaemon(True) 
     stderr_thread.start() 

    if self.stdin: 
     if input is not None: 
      self.stdin.write(input) 
     self.stdin.close() 

    if self.stdout: 
     stdout_thread.join() 
    if self.stderr: 
     stderr_thread.join() 

    # All data exchanged. Translate lists into strings. 
    if stdout is not None: 
     stdout = stdout[0] 
    if stderr is not None: 
     stderr = stderr[0] 

    # Translate newlines, if requested. We cannot let the file 
    # object do the translation: It is based on stdio, which is 
    # impossible to combine with select (unless forcing no 
    # buffering). 
    if self.universal_newlines and hasattr(file, 'newlines'): 
     if stdout: 
      stdout = self._translate_newlines(stdout) 
     if stderr: 
      stderr = self._translate_newlines(stderr) 

    self.wait() 
    return (stdout, stderr) 

ta wykorzystuje wątki odczytu z wielu strumieni jednocześnie. Następnie wywołuje wait() na końcu.

Więc Podsumowując:

  1. Ten przykład odczytuje z jednego strumienia w czasie, a nie czekać na to, aby zakończyć proces.
  2. Ten przykład odczytuje oba strumienie w tym samym czasie za pośrednictwem wątków wewnętrznych i oczekuje na zakończenie procesu.
  3. Ten przykład czeka na zakończenie procesu, a następnie odczytuje jeden strumień na raz. I jak już wspomniałeś ma potencjał do zakleszczenia, jeśli jest zbyt wiele napisanych do strumieni.

Ponadto, nie trzeba te dwie instrukcje importu w twoich 2. i 3. przykładów:

from subprocess import communicate 
from subprocess import wait 

Są to zarówno metody obiektu Popen.