it-swarm.com.de

Wie führt man mehrere Befehle synchron mit einem Subprozess.Popen-Befehl aus?

Ist es möglich, eine beliebige Anzahl von Befehlen nacheinander mit demselben Subprozess-Befehl auszuführen?

Ich brauche jeden Befehl, um zu warten, dass der vorherige Befehl vor der Ausführung abgeschlossen ist, und ich muss alle Befehle in derselben Session/Shell ausführen. Ich brauche das auch, um in Python 2.6, Python 3.5 zu arbeiten. Ich brauche auch den Subprozess-Befehl, um unter Linux, Windows und macOS zu arbeiten (deshalb verwende ich hier nur echo-Befehle).

Siehe nicht funktionierend Code:

import sys
import subprocess

cmds = ['echo start', 'echo mid', 'echo end']

p = subprocess.Popen(cmd=Tuple([item for item in cmds]),
                     stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

for line in iter(p.stdout.readline, b''):
    sys.stdout.flush()
    print(">>> " + line.rstrip())

Wenn dies nicht möglich ist, wie sollte ich vorgehen, um meine Befehle in derselben Session/Shell synchron auszuführen.

7
fredrik

Wenn Sie viele Befehle nacheinander in derselben session/Shell ausführen möchten, müssen Sie eine Shell starten und mit allen Befehlen nacheinander, gefolgt von einer neuen Zeile, speisen und die Pipe bei schließen das Ende. Es ist sinnvoll, wenn einige Befehle keine echten Prozesse sind, sondern Shell-Befehle, die zum Beispiel die Shell-Umgebung ändern könnten.

Beispiel mit Python 2.7 unter Windows:

encoding = 'latin1'
p = subprocess.Popen('cmd.exe', stdin=subprocess.PIPE,
             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for cmd in cmds:
    p.stdin.write(cmd + "\n")
p.stdin.close()
print p.stdout.read()

Damit dieser Code unter Linux ausgeführt werden kann, müssen Sie cmd.exe durch /bin/bash ersetzen und möglicherweise die Kodierung in utf8 ändern. 

Für Python 3 müssten Sie die Befehle kodieren und wahrscheinlich deren Ausgabe dekodieren und beim Drucken Klammern verwenden.

Achtung: Dies funktioniert nur mit geringem Output. Wenn genug Ausgabe vorhanden war, um den Pufferspeicher vor dem Schließen der Standard-Pipe aufzufüllen, würde dieser Code blockieren. Ein robusterer Weg wäre ein zweiter Thread, der die Ausgabe der Befehle liest, um dieses Problem zu vermeiden.

4
Serge Ballesta

Eine mögliche Lösung sieht so aus, als würde sie in derselben Shell laufen:

subprocess.Popen('echo start;echo mid;echo end', Shell=True)

Hinweis - Wenn Sie Ihren Befehl als Zeichenfolge übergeben, muss Shell True .__ sein. Hinweis - Dies funktioniert nur unter Linux. Möglicherweise müssen Sie unter Windows einen ähnlichen Weg finden. 

Ich hoffe es wird helfen.

Von python doc - 

Bei Unix mit Shell = True setzt die Shell standardmäßig auf/bin/sh. Wenn args ein .__ ist. Zeichenfolge, gibt die Zeichenfolge den Befehl an, der über die Shell ausgeführt werden soll . Dies bedeutet, dass die Zeichenfolge genau so formatiert werden muss, wie sie wäre bei Eingabe an der Shell-Eingabeaufforderung.

3
AlokThakur

Dies ist der Antwort von Serge Ballesta ähnlich, aber nicht ganz. Verwenden Sie das für asynchrone Ausführung, wenn Sie sich nicht für die Ergebnisse interessieren. Verwenden Sie meine für die synchrone Verarbeitung und das Sammeln von Ergebnissen. Wie seine Antwort zeige ich hier die Windows-Lösung. Führen Sie einen Bash-Prozess in Linux und nicht in cmd in Windows aus.

from subprocess import Popen, PIPE
process = Popen( "cmd.exe", Shell=False, universal_newlines=True,
                  stdin=PIPE, stdout=PIPE, stderr=PIPE )                             
out, err = process.communicate( commands ) 

USAGE DETAILS: Das hier an die process.communicate-Methode übergebene commands-Argument ist ein Newline-Trennzeichen string. Wenn Sie zum Beispiel nur den Inhalt einer Batchdatei in eine Zeichenfolge einlesen, können Sie sie auf diese Weise ausführen, da sie bereits die Zeilenumbrüche enthält. Wichtig : Ihre Zeichenfolge muss mit einem Zeilenvorschub "\n" enden. Wenn dies nicht der Fall ist, wird der letzte Befehl nicht ausgeführt. Genauso, als hätten Sie es in Ihre Eingabeaufforderung eingegeben, aber am Ende nicht enter gedrückt. Am Ende der zurückgegebenen Standardausgabe sehen Sie jedoch eine mysteriöse More?-Zeile. (das ist die Ursache, wenn Sie dies antreffen).

process.communicate wird per Definition synchron ausgeführt und gibt die Meldungen stdout und stderr zurück (wenn Sie sie in Ihrem Popen-Konstruktor an subprocess.PIPE weitergeleitet haben).

Wenn Sie auf diese Weise einen cmd.exe-Prozess erstellen und einen String übergeben, werden die Ergebnisse genau so angezeigt, als würden Sie ein Eingabeaufforderungsfenster öffnen und Befehle eingeben. Und das meine ich wörtlich. Wenn Sie dies testen, werden Sie feststellen, dass das zurückgegebene stdout Ihre Befehle enthält. (Es ist NICHT wichtig, ob Sie einen @echo off-Code verwenden, wenn Sie eine Batchdatei ausführen.).

Tipps für diejenigen, die Wert auf "saubere" Ergebnisse legen: 

  • @echo off unterdrückt zwar nicht, dass Ihre Befehle in dieser zurückgegebenen Zeichenfolge angezeigt werden, entfernt jedoch zusätzliche Zeilenumbrüche, die sonst ihren Weg finden. (universal_newlines = True entfernt einen anderen Satz davon)

  • Durch das Einfügen eines @-Symbolpräfixes in Ihre Befehle können sie weiterhin ausgeführt werden. In einem "normalen" Batch-Prozess ist dies der zeilenweise Weg, um Ihre Befehle zu "verstecken". In diesem Zusammenhang ist dies eine sichere Markierung, mit der Sie Standard-Zeilen finden können, die Sie entfernen möchten. (wenn einer so geneigt war)

  • Der cmd.exe "Header" erscheint in Ihrer Ausgabe (was die Version von Windows usw. angibt). Da Sie Ihren Befehlssatz wahrscheinlich mit @echo off starten möchten, um die zusätzlichen Zeilenumbrüche auszuschneiden, können Sie auch herausfinden, wo die Kopfzeilen aufgehört haben und Ihre Befehle/Ergebnisse begonnen haben.

Um Bedenken hinsichtlich einer "großen" Ausgabe zu befriedigen, die die Rohrleitungen füllt und Ihnen Probleme bereitet, müssen Sie zunächst einmal eine RIESIGE Datenmenge erhalten, die dazu führt, dass dies ein Problem darstellt - mehr als die meisten Menschen in ihren Anwendungsfällen. Zweitens, wenn es wirklich ein Problem ist, öffnen Sie einfach eine Datei zum Schreiben und übergeben Sie diesen Datei-Handle (den Verweis auf das Dateiobjekt) an stdout/err anstelle von PIPE. Machen Sie dann mit der von Ihnen erstellten Datei, was Sie möchten.

2
BuvinJ

Dieser arbeitet in Python 2.7 und sollte auch in Windows funktionieren. Für Python> 3 ist wahrscheinlich eine kleine Verfeinerung erforderlich. 

Die erzeugte Ausgabe ist (mit date und sleep kann man leicht sehen, dass die Befehle in Zeile ausgeführt werden):

>>>Die Sep 27 12:47:52 CEST 2016
>>>
>>>Die Sep 27 12:47:54 CEST 2016

Wie Sie sehen, werden die Befehle in einer Reihe ausgeführt. 

    import sys
    import subprocess
    import shlex

    cmds = ['date', 'sleep 2', 'date']

    cmds = [shlex.split(x) for x in cmds]

    outputs =[]
    for cmd in cmds:
        outputs.append(subprocess.Popen(cmd,
                                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate())


    for line in outputs:
        print ">>>" + line[0].strip()

Dies ist, was ich mit @Marichyasana Antwort verschmelze:

import sys
import os


def run_win_cmds(cmds):

    @Marichyasana code (+/-)

def run_unix_cmds(cmds):

    import subprocess
    import shlex


    cmds = [shlex.split(x) for x in cmds]

    outputs =[]
    for cmd in cmds:
        outputs.append(subprocess.Popen(cmd,
                                        stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate())


    rc = ''
    for line in outputs:
        rc +=  line[0].strip()+'\n'

    return rc


cmds = ['date', 'sleep 2', 'date']

if os.name == 'nt':
     run_win_cmds(cmds)
Elif os.name == 'posix':
    run_unix_cmds(cmds)

Fragen Sie, ob diese nicht Ihren Bedürfnissen entsprechen! ;)

1

Hier ist eine Funktion (und Hauptfunktion zum Ausführen), die ich verwende. Ich würde sagen, dass Sie es für Ihr Problem verwenden können. Und es ist flexibel.

# processJobsInAList.py
# 2016-09-27   7:00:00 AM   Central Daylight Time 

import win32process, win32event

def CreateMyProcess2(cmd):
    ''' create process width no window that runs a command with arguments
    and returns the process handle'''
    si   = win32process.STARTUPINFO()
    info = win32process.CreateProcess(
        None,      # AppName
        cmd,       # Command line
        None,      # Process Security
        None,      # Thread Security
        0,         # inherit Handles?
        win32process.NORMAL_PRIORITY_CLASS,
        None,      # New environment
        None,      # Current directory
        si)        # startup info
    # info is Tuple (hProcess, hThread, processId, threadId)
    return info[0]

if __== '__main__' :
    ''' create/run a process for each list element in "cmds"
    output may be out of order because processes run concurrently '''

    cmds=["echo my","echo heart","echo belongs","echo to","echo daddy"]
    handles    = []
    for i in range(len(cmds)):
        cmd    = 'cmd /c ' + cmds[i]
        handle = CreateMyProcess2(cmd)
        handles.append(handle)

    rc = win32event.WaitForMultipleObjects( handles, 1, -1)  # 1 wait for all, -1 wait infinite
    print 'return code ',rc

ausgabe:
Herz
meine
gehört
zu
Vati
Rückkehrcode 0 

UPDATE: Wenn Sie denselben Prozess ausführen möchten, werden die Dinge für Sie serialisiert:
1) Entferne die Zeile: handle.append (handle)
2) Ersetzen Sie die Variable "handle" anstelle der Liste "handle" in der "WaitFor" -Zeile
3) Ersetzen Sie WaitForSingleObject durch WaitForMultipleObjects

1
Marichyasana