Vraag Een extern commando in Python aanroepen


Hoe kan ik vanuit een Python-script een extern commando bellen (alsof ik het op de Unix-shell of Windows-opdrachtprompt heb getypt)?


3622
2017-09-18 01:35


oorsprong


antwoorden:


Kijk naar de subprocesmodule in de standaardbibliotheek:

from subprocess import call
call(["ls", "-l"])

Het voordeel van deelproces vs. systeem is dat het flexibeler is (je kunt de stdout, stderr, de "echte" statuscode krijgen, een betere foutafhandeling, enz ...).

De officiële documentatie beveelt de deelproces module over het alternatieve os.systeem ():

De deelproces module biedt krachtiger faciliteiten voor het uitzetten van nieuwe processen en het ophalen van hun resultaten; het gebruik van die module heeft de voorkeur boven het gebruik van deze functie [os.system()].

De "Oudere functies vervangen door de subprocesmodule"sectie in de deelproces documentatie kan enkele nuttige recepten bevatten.

Officiële documentatie over de deelproces module:


3491
2017-09-18 01:39



Hier volgt een samenvatting van de manieren om externe programma's te bellen en de voor- en nadelen van elk programma:

  1. os.system("some_command with args") geeft de opdracht en argumenten door aan de shell van uw systeem. Dit is leuk omdat je op deze manier meerdere opdrachten tegelijk kunt uitvoeren en leidingen kunt instellen en input / output omleiding. Bijvoorbeeld:

    os.system("some_command < input_file | another_command > output_file")  
    

    Hoewel dit handig is, moet je het ontsnappen van shell-karakters zoals spaties enz. Handmatig afhandelen. Dit laat je ook commando's uitvoeren die eenvoudig shell-commando's zijn en niet daadwerkelijk externe programma's. Zien de documentatie.

  2. stream = os.popen("some_command with args") zal hetzelfde doen als os.system behalve dat het je een bestand-achtig object geeft dat je kunt gebruiken om toegang te krijgen tot standaard invoer / uitvoer voor dat proces. Er zijn 3 andere varianten van popen die allemaal iets anders omgaan met de i / o. Als u alles doorgeeft als een tekenreeks, wordt uw opdracht doorgegeven aan de shell; als je ze als een lijst doorgeeft, hoef je je geen zorgen te maken over het ontsnappen van wat dan ook. Zien de documentatie.

  3. De Popen klasse van de subprocess module. Dit is bedoeld als vervanging voor os.popen maar heeft het nadeel dat het enigszins gecompliceerder is omdat het zo veelomvattend is. U zou bijvoorbeeld zeggen:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    in plaats van:

    print os.popen("echo Hello World").read()
    

    maar het is leuk om alle opties daar in één uniforme klasse te hebben in plaats van 4 verschillende popen-functies. Zien de documentatie.

  4. De call functie van de subprocess module. Dit is eigenlijk net als het Popen klasse en neemt alle dezelfde argumenten, maar wacht gewoon totdat de opdracht is voltooid en geeft u de retourcode. Bijvoorbeeld:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    Zien de documentatie.

  5. Als je met Python 3.5 of hoger werkt, kun je het nieuwe gebruiken subprocess.run functie, die veel op het bovenstaande lijkt maar nog flexibeler is en een a teruggeeft CompletedProcess object wanneer het commando klaar is met uitvoeren.

  6. De os-module heeft ook alle functies voor vork / exec / spawn die u in een C-programma zou hebben, maar ik raad niet aan ze direct te gebruiken.

De subprocess module moet waarschijnlijk zijn wat u gebruikt.

Houd er ten slotte rekening mee dat voor alle methoden waarbij u de laatste opdracht doorgeeft die door de shell moet worden uitgevoerd als een tekenreeks, u verantwoordelijk bent om eraan te ontsnappen. Er zijn serieuze veiligheidsimplicaties als een deel van de reeks dat u passeert niet volledig kan worden vertrouwd. Bijvoorbeeld als een gebruiker een deel van de string invoert. Als u niet zeker bent, gebruik deze methoden dan alleen met constanten. Om u een idee te geven van de implicaties, overweeg dan deze code:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

en stel je voor dat de gebruiker invoert: "mijn moeder hield niet van mij && rm -rf /".


2466
2017-09-18 13:11



Ik gebruik meestal:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

Je bent vrij om te doen wat je wilt met de stdout gegevens in de pijp. In feite kunt u eenvoudig die parameters weglaten (stdout= en stderr=) en het zal zich gedragen als os.system().


255
2017-09-18 18:20



Enkele hints voor het loskoppelen van het onderliggende proces van het opgeroepen proces (starten van het onderliggende proces op de achtergrond).

Stel dat u een lange taak vanuit een CGI-script wilt starten, dat wil zeggen dat het onderliggende proces langer moet leven dan het uitvoeringsproces van het CGI-script.

Het klassieke voorbeeld uit de documentdocumenten van het subproces is:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

Het idee hier is dat u niet in de regel 'oproepsubproces' wilt wachten totdat de longtask.py is voltooid. Maar het is niet duidelijk wat er gebeurt na de regel 'wat meer code hier' uit het voorbeeld.

Mijn doelplatform was freebsd, maar de ontwikkeling was op Windows, dus ik zag het probleem eerst op Windows.

In Windows (win xp) wordt het bovenliggende proces pas voltooid nadat longtask.py zijn werk heeft voltooid. Het is niet wat je wilt in CGI-script. Het probleem is niet specifiek voor Python, in PHP-community zijn de problemen hetzelfde.

De oplossing is om DETACHED_PROCESS te passeren Process Creation Flag naar de onderliggende CreateProcess-functie in de win-API. Als je toevallig pywin32 hebt geïnstalleerd, kun je de vlag importeren uit de module win32process, anders zou je het zelf moeten definiëren:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun in een opmerking hieronder merkt op dat de semantisch correcte vlag CREATE_NEW_CONSOLE (0x00000010) * / is

Op freebsd hebben we nog een probleem: wanneer het ouderproces is voltooid, worden ook de onderliggende processen voltooid. En dat is ook niet wat je wilt in CGI-script. Sommige experimenten lieten zien dat het probleem leek te zijn in het delen van sys.stdout. En de werkende oplossing was de volgende:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

Ik heb de code niet op andere platforms gecontroleerd en weet de reden van het gedrag op freebsd niet. Als iemand het weet, deel dan uw ideeën. Googlen bij het starten van achtergrondprocessen in Python werpt nog geen licht.


157
2018-02-12 10:15



Ik raad u aan de subprocesmodule te gebruiken in plaats van os.system omdat het shell voor u ontloopt en daarom veel veiliger is: http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

97
2017-09-18 01:42



import os
cmd = 'ls -al'
os.system(cmd)

Als u de resultaten van de opdracht wilt retourneren, kunt u gebruiken os.popen. Dit is echter gedeprecieerd sinds versie 2.6 in het voordeel van de subprocesmodule, welke andere antwoorden goed hebben gedekt.


93
2017-09-18 01:37



import os
os.system("your command")

Merk op dat dit gevaarlijk is, omdat het commando niet is schoongemaakt. Ik laat het aan u om te googlen voor de relevante documentatie over de modules 'os' en 'sys'. Er zijn een heleboel functies (exec * en spawn *) die vergelijkbare dingen doen.


82
2017-09-18 01:37



Ik gebruik altijd fabric voor deze dingen zoals:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Maar dit lijkt een goede tool te zijn: sh (Python-subprocesinterface).

Kijk een voorbeeld:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

51
2018-03-13 00:12



Controleer ook de "pexpect" Python-bibliotheek.

Het maakt het mogelijk om externe programma's / commando's interactief te besturen, zelfs ssh, ftp, telnet, enz. Je kunt gewoon iets typen als:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

51
2017-10-07 07:09



Er zijn veel verschillende bibliotheken waarmee je externe commando's kunt aanroepen met Python. Voor elke bibliotheek heb ik een beschrijving gegeven en een voorbeeld getoond van het aanroepen van een extern commando. De opdracht die ik als voorbeeld gebruikte is ls -l (lijst alle bestanden). Als u meer wilt weten over de bibliotheken die ik heb vermeld en de documentatie voor elk van hen heb gekoppeld.

bronnen:

Dit zijn alle bibliotheken:

Hopelijk helpt dit je om een ​​beslissing te nemen over welke bibliotheek je moet gebruiken :)

deelproces

Met subproces kunt u externe opdrachten aanroepen en deze verbinden met hun invoer / uitvoer / foutpijpen (stdin, stdout en stderr). Subproces is de standaardkeuze voor het uitvoeren van opdrachten, maar soms zijn andere modules beter.

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

os

os wordt gebruikt voor "besturingssysteemafhankelijke functionaliteit". Het kan ook worden gebruikt om externe opdrachten mee te bellen os.system en os.popen (Opmerking: er is ook een subproces.popen). os zal altijd de shell draaien en is een eenvoudig alternatief voor mensen die het niet nodig hebben of niet weten hoe te gebruiken subprocess.run.

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

sh

sh is een subprocesinterface waarmee u programma's kunt oproepen alsof het functies zijn. Dit is handig als u een opdracht meerdere keren wilt uitvoeren.

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

lood

plumbum is een bibliotheek voor "scriptachtige" Python-programma's. U kunt programma's zoals functies oproepen als in sh. Plumbum is handig als u een pijplijn zonder shell wilt uitvoeren.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

pexpect

Met pexpect kunt u toepassingen voor kinderen spawnen, besturen en patronen in hun uitvoer vinden. Dit is een beter alternatief voor subprocessen voor opdrachten die een tty verwachten op Unix.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

kleding stof

fabric is een Python 2.5 en 2.7 library. Hiermee kunt u lokale en externe shell-opdrachten uitvoeren. Fabric is een eenvoudig alternatief voor het uitvoeren van opdrachten in een beveiligde shell (SSH)

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

gezant

gezant staat bekend als "subproces voor mensen". Het wordt gebruikt als een gemaksverpakking rond de subprocess module.

r = envoy.run("ls -l") # Run command
r.std_out # get output

commando's

commands bevat wrapper-functies voor os.popen, maar het is sindsdien uit Python 3 verwijderd subprocess is een beter alternatief.

De bewerking was gebaseerd op de opmerking van J.F. Sebastian.


46
2017-10-29 14:02