web-dev-qa-db-fra.com

Python sous-processus Popen.communicate () équivalent à Popen.stdout.read ()?

Question très spécifique (j'espère): Quelles sont les différences entre les trois codes suivants?

(Je m'attends à ce que le premier n'attende pas la fin du processus enfant, tandis que le deuxième et le troisième le font. Mais je dois être sûr que c'est la différence seulement. ..)

Je me réjouis également d'autres remarques/suggestions (bien que je sois déjà bien au courant du Shell=True dangers et limitations multiplateformes)

Notez que j'ai déjà lu Interaction du sous-processus Python, pourquoi mon processus fonctionne-t-il avec Popen.communicate, mais pas Popen.stdout.read ()? et que je ne veux/n'ai pas besoin d'interagir avec le programme après .

Notez également que j'ai déjà lu Alternatives à Python Popen.communicate ()? mais que je ne l'ai pas vraiment compris ...

Enfin, notez que je suis conscient que quelque part il y a un risque de blocage lorsqu'un tampon est rempli avec une sortie en utilisant une méthode, mais je me suis perdu en cherchant des explications claires sur Internet ...

Premier code:

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

Deuxième code:

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

Troisième code:

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

Merci.

29
Christophe

Si vous regardez la source de subprocess.communicate(), cela montre un exemple parfait de la différence:

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)

Vous pouvez voir que communicate utilise les appels de lecture à stdout et stderr, et appelle également wait(). C'est juste une question d'ordre des opérations. Dans votre cas, car vous utilisez PIPE pour stdout et stderr, il va dans _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)

Cela utilise des threads pour lire à partir de plusieurs flux à la fois. Ensuite, il appelle wait() à la fin.

Donc, pour résumer:

  1. Cet exemple lit un flux à la fois et n'attend pas qu'il termine le processus.
  2. Cet exemple lit les deux flux en même temps via les threads internes et attend qu'il termine le processus.
  3. Cet exemple attend la fin du processus, puis lit un flux à la fois. Et comme vous l'avez mentionné, il y a un risque de blocage s'il y a trop d'écrit dans les flux.

De plus, vous n'avez pas besoin de ces deux instructions d'importation dans vos 2e et 3e exemples:

from subprocess import communicate
from subprocess import wait

Ce sont les deux méthodes de l'objet Popen.

38
jdi