Chiamando un comando esterno da Python


Risposte:


4696

Guarda il modulo di sottoprocesso nella libreria standard:

import subprocess
subprocess.run(["ls", "-l"])

Il vantaggio di subprocesscontro systemè che è più flessibile (è possibile ottenere il stdout, stderr, il codice di stato "reale", migliore gestione degli errori, ecc ...).

La documentazione ufficiale raccomanda il subprocessmodulo rispetto all'alternativa os.system():

Il subprocessmodulo fornisce strutture più potenti per generare nuovi processi e recuperare i loro risultati; usare quel modulo è preferibile usare questa funzione [ os.system()].

La sezione Sostituzione delle funzioni precedenti con il modulo del sottoprocesso nella subprocessdocumentazione può contenere alcune ricette utili.

Per le versioni di Python precedenti alla 3.5, usare call:

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

C'è un modo per usare la sostituzione variabile? IE, ho provato a fare echo $PATHusando call(["echo", "$PATH"]), ma ha fatto eco alla stringa letterale $PATHinvece di fare qualsiasi sostituzione. So che potrei ottenere la variabile d'ambiente PATH, ma mi chiedo se c'è un modo semplice per far sì che il comando si comporti esattamente come se l'avessi eseguito in bash.
Kevin Wheeler,

@KevinWheeler Dovrai usarlo shell=Trueper farlo funzionare.
SethMMorton,

39
@KevinWheeler NON dovresti usare shell=True, a questo scopo Python viene fornito con os.path.expandvars . Nel tuo caso si può scrivere: os.path.expandvars("$PATH"). @SethMMorton ti preghiamo di riconsiderare il tuo commento -> Perché non usare shell = True
Murmel

chiama il blocco? cioè se voglio eseguire più comandi in un forciclo come posso farlo senza bloccare il mio script Python? Non mi interessa l'output del comando, voglio solo eseguirne molti.
Charlie Parker,

5
Se si desidera creare un elenco da un comando con parametri , un elenco che può essere utilizzato con subprocessquando shell=False, quindi utilizzare shlex.splitper un modo semplice di fare questo docs.python.org/2/library/shlex.html#shlex.split
Daniel F,

2985

Ecco un riepilogo dei modi per chiamare programmi esterni e i vantaggi e gli svantaggi di ciascuno:

  1. os.system("some_command with args")passa il comando e gli argomenti alla shell del sistema. Questo è utile perché puoi effettivamente eseguire più comandi contemporaneamente in questo modo e impostare pipe e reindirizzamento input / output. Per esempio:

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

Tuttavia, sebbene ciò sia conveniente, devi gestire manualmente l'escaping di caratteri shell come spazi, ecc. D'altra parte, questo ti consente anche di eseguire comandi che sono semplicemente comandi shell e non in realtà programmi esterni. Vedere la documentazione .

  1. stream = os.popen("some_command with args")farà la stessa cosa, os.systemtranne per il fatto che ti dà un oggetto simile a un file che puoi usare per accedere all'input / output standard per quel processo. Esistono altre 3 varianti di popen che gestiscono tutti gli I / O in modo leggermente diverso. Se passi tutto come una stringa, il tuo comando viene passato alla shell; se li passi come un elenco, non devi preoccuparti di sfuggire a nulla. Vedere la documentazione .

  2. La Popenclasse del subprocessmodulo. Questo è inteso come un sostituto per os.popenma ha il rovescio della medaglia di essere leggermente più complicato in virtù di essere così completo. Ad esempio, diresti:

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

    invece di:

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

    ma è bello avere tutte le opzioni lì in una classe unificata invece di 4 diverse funzioni popen. Vedere la documentazione .

  3. La callfunzione dal subprocessmodulo. Questo è fondamentalmente proprio come la Popenclasse e accetta tutti gli stessi argomenti, ma attende semplicemente fino al completamento del comando e ti dà il codice di ritorno. Per esempio:

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

    Vedere la documentazione .

  4. Se utilizzi Python 3.5 o subprocess.runversioni successive, puoi utilizzare la nuova funzione, che è molto simile alla precedente ma ancora più flessibile e restituisce un CompletedProcessoggetto al termine dell'esecuzione del comando.

  5. Il modulo os ha anche tutte le funzioni fork / exec / spawn che avresti in un programma C, ma non ti consiglio di usarle direttamente.

Il subprocessmodulo dovrebbe probabilmente essere quello che usi.

Infine, tieni presente che per tutti i metodi in cui passi il comando finale deve essere eseguito dalla shell come stringa e sei responsabile della sua fuga. Ci sono serie implicazioni per la sicurezza se qualsiasi parte della stringa che passi non può essere completamente attendibile. Ad esempio, se un utente sta inserendo una / parte della stringa. Se non si è sicuri, utilizzare questi metodi solo con costanti. Per darti un suggerimento delle implicazioni, considera questo codice:

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

e immagina che l'utente inserisca qualcosa "mia mamma non mi amava && rm -rf /" che poteva cancellare l'intero filesystem.


22
Bella risposta / spiegazione. In che modo questa risposta giustifica il motto di Python come descritto in questo articolo? fastcompany.com/3026446/… "Stilisticamente, Perl e Python hanno filosofie diverse. I motti più noti di Perl è" C'è più di un modo per farlo ". Python è progettato per avere un modo ovvio per farlo" Sembra che dovrebbe essere l'altro modo! In Perl conosco solo due modi per eseguire un comando: usare il back-tick o open.
Jean,

12
Se si utilizza Python 3.5+, utilizzare subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
phoenix

4
Ciò che in genere è necessario sapere è cosa viene fatto con STDOUT e STDERR del processo figlio, perché se vengono ignorati, in alcune condizioni (abbastanza comuni), alla fine il processo figlio emetterà una chiamata di sistema per scrivere a STDOUT (anche STDERR?) che supererebbe il buffer di output fornito per il processo dal sistema operativo e il sistema operativo lo farà bloccare fino a quando alcuni processi non vengono letti da quel buffer. Quindi, con le modalità attualmente consigliate subprocess.run(..), cosa fa esattamente "Questo non cattura stdout o stderr di default." implicare? Che dire di subprocess.check_output(..)e STDERR?
Evgeni Sergeev,

2
@Pitto sì, ma non è quello che viene eseguito dall'esempio. Notare il echodavanti alla stringa passata a Popen? Quindi sarà il comando completo echo my mama didnt love me && rm -rf /.
Chris Arndt,

6
Questo è probabilmente il modo sbagliato. La maggior parte delle persone ha solo bisogno subprocess.run()o dei suoi fratelli più grandi subprocess.check_call()et al. Per i casi in cui questi non sono sufficienti, vedere subprocess.Popen(). os.popen()forse non dovrebbe essere menzionato affatto, o venire anche dopo "hacking il proprio codice fork / exec / spawn".
Tripleee

358

Implementazione tipica:

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()

Sei libero di fare quello che vuoi con i stdoutdati nella pipe. In effetti, puoi semplicemente omettere quei parametri ( stdout=e stderr=) e si comporterà come os.system().


44
.readlines()legge tutte le righe contemporaneamente, cioè si blocca fino a quando il sottoprocesso non esce (chiude la sua estremità del tubo). Per leggere in tempo reale (se non ci sono problemi di buffering) potresti:for line in iter(p.stdout.readline, ''): print line,
jfs

1
Potresti approfondire cosa intendi con "se non ci sono problemi di buffering"? Se il processo si blocca definitivamente, anche la chiamata al sottoprocesso si blocca. Lo stesso potrebbe accadere anche con il mio esempio originale. Cos'altro potrebbe accadere rispetto al buffering?
EmmEff,

15
il processo figlio può usare il buffering a blocchi in modalità non interattiva invece del buffering di linea, quindi p.stdout.readline()(nota: no salla fine) non vedrà alcun dato finché il figlio non riempie il suo buffer. Se il bambino non produce molti dati, l'output non sarà in tempo reale. Vedi il secondo motivo in Q: Perché non usare semplicemente una pipe (popen ())? . In questa risposta vengono fornite alcune soluzioni alternative (pexpect, pty, stdbuf)
jfs

4
il problema del buffering è importante solo se si desidera l'output in tempo reale e non si applica al codice che non stampa nulla fino alla ricezione di tutti i dati
jfs

3
Questa risposta andava bene per il suo tempo, ma non dovremmo più raccomandare Popenper compiti semplici. Questo specifica anche inutilmente shell=True. Prova una delle subprocess.run()risposte.
Tripleee

230

Alcuni suggerimenti su come staccare il processo figlio da quello chiamante (avviare il processo figlio in background).

Supponiamo di voler avviare un'attività lunga da uno script CGI. Cioè, il processo figlio dovrebbe durare più a lungo del processo di esecuzione dello script CGI.

L'esempio classico dalla documentazione del modulo di sottoprocesso è:

import subprocess
import sys

# Some code here

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

# Some more code here

L'idea qui è che non si desidera attendere nella riga 'call subprocess' fino al termine di longtask.py. Ma non è chiaro cosa succede dopo la riga "altro codice qui" dall'esempio.

La mia piattaforma di destinazione era FreeBSD, ma lo sviluppo era su Windows, quindi ho affrontato prima il problema su Windows.

Su Windows (Windows XP), il processo padre non terminerà fino a quando longtask.py non avrà terminato il suo lavoro. Non è quello che vuoi in uno script CGI. Il problema non è specifico di Python; nella comunità PHP i problemi sono gli stessi.

La soluzione è passare il Flag di creazione processo DETACHED_PROCESS alla funzione CreateProcess sottostante nell'API di Windows. Se hai installato pywin32, puoi importare il flag dal modulo win32process, altrimenti dovresti definirlo tu stesso:

DETACHED_PROCESS = 0x00000008

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

/ * UPD 2015.10.27 @eryksun in un commento sotto le note, che il flag semanticamente corretto è CREATE_NEW_CONSOLE (0x00000010) * /

Su FreeBSD abbiamo un altro problema: quando il processo padre è finito, termina anche i processi figlio. E non è nemmeno quello che vuoi in uno script CGI. Alcuni esperimenti hanno dimostrato che il problema sembrava essere nella condivisione di sys.stdout. E la soluzione di lavoro era la seguente:

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

Non ho controllato il codice su altre piattaforme e non conosco i motivi del comportamento su FreeBSD. Se qualcuno lo sa, per favore condividi le tue idee. Cercare su Google l'avvio di processi in background in Python non fa ancora luce.


ho notato una possibile "stranezza" con lo sviluppo di app py2exe in pydev + eclipse. sono stato in grado di dire che lo script principale non era staccato perché la finestra di output di eclipse non stava terminando; anche se lo script viene eseguito fino al completamento, è ancora in attesa di resi. ma, quando ho provato a compilare un eseguibile py2exe, si verifica il comportamento previsto (esegue i processi come distaccati, quindi si chiude). non ne sono sicuro, ma il nome dell'eseguibile non è più nell'elenco dei processi. funziona per tutti gli approcci (os.system ("start *"), os.spawnl con os.P_DETACH, sottoprocessi, ecc.)
maranas

1
potresti anche aver bisogno del flag CREATE_NEW_PROCESS_GROUP. Vedi Popen in attesa del processo figlio anche quando il figlio immediato è terminato
jfs

5
Quanto segue non è corretto: "[o] n windows (win xp), il processo parent non terminerà fino a quando longtask.py non avrà terminato il suo lavoro". Il genitore uscirà normalmente, ma la finestra della console (istanza conhost.exe) si chiude solo all'uscita dell'ultimo processo collegato e il figlio potrebbe aver ereditato la console del genitore. L'impostazione DETACHED_PROCESSin creationflagsevita questo, impedendo al bambino di ereditare o la creazione di una console. Se invece desideri una nuova console, utilizza CREATE_NEW_CONSOLE(0x00000010).
Eryk Sun,

1
Non intendevo dire che l'esecuzione come processo distaccato non è corretta. Detto questo, potrebbe essere necessario impostare gli handle standard su file, pipe o os.devnullperché alcuni programmi della console si chiudono con un errore altrimenti. Creare una nuova console quando si desidera che il processo figlio interagisca con l'utente contemporaneamente al processo padre. Sarebbe confuso provare a fare entrambi in un'unica finestra.
Eryk Sun,

1
non esiste un modo indipendente dal sistema operativo per eseguire il processo in background?
Charlie Parker,

152
import os
os.system("your command")

Si noti che questo è pericoloso, poiché il comando non è stato pulito. Lascio a te Google per la documentazione pertinente sui moduli "os" e "sys". Ci sono un sacco di funzioni (exec * e spawn *) che faranno cose simili.


6
Non ho idea di cosa intendessi quasi un decennio fa (controlla la data!), Ma se dovessi indovinare, sarebbe che non è stata effettuata alcuna convalida.
nimish,

1
Questo dovrebbe ora indicare subprocessuna soluzione leggermente più versatile e portatile. L'esecuzione di comandi esterni è ovviamente intrinsecamente non portabile (è necessario assicurarsi che il comando sia disponibile su ogni architettura che è necessario supportare) e passare l'input dell'utente come comando esterno è intrinsecamente pericoloso.
Tripleee

1
Nota il timestamp su questo ragazzo: la risposta "corretta" ha 40 volte i voti ed è la risposta # 1.
nimish,

L'unica soluzione che ha funzionato per me per l'esecuzione di cose NodeJS.
Nikolay Shindarov,

148

Consiglio di usare il modulo di sottoprocesso invece di os.system perché fa scappare la shell per te ed è quindi molto più sicuro.

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

Se si desidera creare un elenco da un comando con parametri , un elenco che può essere utilizzato con subprocessquando shell=False, quindi utilizzare shlex.splitper un modo semplice di fare questo docs.python.org/2/library/shlex.html#shlex.split ( è il modo raccomandato secondo i documenti docs.python.org/2/library/subprocess.html#popen-constructor )
Daniel F

6
Questo non è corretto: " fa scappare la shell per te ed è quindi molto più sicuro ". il sottoprocesso non esegue l'escaping della shell, il sottoprocesso non passa il comando attraverso la shell, quindi non è necessario eseguire la escape della shell.
Lie Ryan,

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

Se si desidera restituire i risultati del comando, è possibile utilizzare os.popen. Tuttavia, questo è deprecato dalla versione 2.6 a favore del modulo di sottoprocesso , che altre risposte hanno coperto bene.


10
popen è deprecato a favore del sottoprocesso .
Fox Wilson,

Puoi anche salvare il tuo risultato con la chiamata os.system, poiché funziona come la stessa shell UNIX, come ad esempio os.system ('ls -l> test2.txt')
Stefan Gruenwald,

97

Esistono molte librerie diverse che ti consentono di chiamare comandi esterni con Python. Per ogni libreria ho fornito una descrizione e mostrato un esempio di chiamata a un comando esterno. Il comando che ho usato come esempio è ls -l(elenca tutti i file). Se vuoi saperne di più su una qualsiasi delle librerie che ho elencato e collegato la documentazione per ciascuna di esse.

fonti:

Queste sono tutte le librerie:

Spero che questo ti aiuti a prendere una decisione su quale libreria usare :)

sottoprocesso

Il sottoprocesso consente di chiamare comandi esterni e collegarli alle relative pipe di input / output / error (stdin, stdout e stderr). Il sottoprocesso è la scelta predefinita per l'esecuzione dei comandi, ma a volte altri moduli sono migliori.

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 è usato per "funzionalità dipendente dal sistema operativo". Può anche essere usato per chiamare comandi esterni con os.systeme os.popen(Nota: c'è anche un subprocess.popen). os eseguirà sempre la shell ed è una semplice alternativa per le persone che non ne hanno bisogno o non sanno come utilizzarle subprocess.run.

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

sh

sh è un'interfaccia di sottoprocesso che consente di chiamare i programmi come se fossero funzioni. Ciò è utile se si desidera eseguire un comando più volte.

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

plumbum

plumbum è una libreria per programmi Python "simili a script". Puoi chiamare programmi come funzioni come in sh. Plumbum è utile se si desidera eseguire una pipeline senza shell.

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

pexpect

pexpect ti consente di generare applicazioni figlio, controllarle e trovare modelli nel loro output. Questa è una migliore alternativa al sottoprocesso per i comandi che prevedono un tty su 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')

tessuto

fabric è una libreria Python 2.5 e 2.7. Ti permette di eseguire comandi shell locali e remoti. Fabric è una semplice alternativa per eseguire comandi in una shell sicura (SSH)

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

inviato

inviato è noto come "sottoprocesso per l'uomo". È usato come involucro pratico attorno al subprocessmodulo.

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

comandi

commandscontiene funzioni wrapper per os.popen, ma è stato rimosso da Python 3 poiché subprocessè un'alternativa migliore.

La modifica si basava sul commento di JF Sebastian.


74

Uso sempre fabricper queste cose come:

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

Ma questo sembra essere un buon strumento: sh(interfaccia sottoprocesso Python) .

Guarda un esempio:

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

73

Controlla anche la libreria "pexpect" di Python.

Consente il controllo interattivo di programmi / comandi esterni, anche ssh, ftp, telnet, ecc. Puoi semplicemente digitare qualcosa come:

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

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

70

Con la libreria standard

Utilizzare il modulo di sottoprocesso (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

È il modo standard raccomandato. Tuttavia, attività più complesse (pipe, output, input, ecc.) Possono essere noiose da costruire e scrivere.

Nota sulla versione di Python: se si sta ancora utilizzando Python 2, subprocess.call funziona in modo simile.

Suggerimento: shlex.split può aiutarti ad analizzare il comando per run, calle altre subprocessfunzioni nel caso tu non voglia (o non puoi!) Fornirle sotto forma di liste:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

Con dipendenze esterne

Se non ti interessano le dipendenze esterne, usa il piombo :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

È il miglior subprocessinvolucro. È multipiattaforma, cioè funziona sia su sistemi Windows che Unix. Installa da pip install plumbum.

Un'altra libreria popolare è sh :

from sh import ifconfig
print(ifconfig('wlan0'))

Tuttavia, è stato sheliminato il supporto di Windows, quindi non è eccezionale come una volta. Installa da pip install sh.


69

Se hai bisogno dell'output del comando che stai chiamando, puoi usare subprocess.check_output (Python 2.7+).

>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

Nota anche il parametro shell .

Se la shell è True, il comando specificato verrà eseguito attraverso la shell. Ciò può essere utile se si utilizza Python principalmente per il flusso di controllo avanzato che offre sulla maggior parte delle shell di sistema e si desidera ancora un comodo accesso ad altre funzionalità della shell come pipe di shell, caratteri jolly di file, espansione di variabili di ambiente ed espansione di ~ a casa dell'utente directory. Tuttavia, si noti che Python si offre implementazioni di molti a guscio caratteristiche (in particolare glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), e shutil).


1
Si noti che check_outputrichiede un elenco anziché una stringa. Se non ti affidi agli spazi tra virgolette per rendere valida la tua chiamata, il modo più semplice e leggibile per farlo è subprocess.check_output("ls -l /dev/null".split()).
Bruno Bronosky,

56

Ecco come eseguo i miei comandi. Questo codice ha praticamente tutto ciò di cui hai bisogno

from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()

3
Penso che sia accettabile per i comandi hardcoded, se aumenta la leggibilità.
Adam Matan,

54

Aggiornare:

subprocess.runè l'approccio consigliato a partire da Python 3.5 se il codice non deve mantenere la compatibilità con le versioni precedenti di Python. È più coerente e offre una facilità d'uso simile a Envoy. (Le tubazioni non sono altrettanto semplici. Vedi questa domanda per come .)

Ecco alcuni esempi dalla documentazione .

Esegui un processo:

>>> subprocess.run(["ls", "-l"])  # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

Rilancio in caso di esito negativo:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Cattura output:

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

Risposta originale:

Consiglio di provare Envoy . È un wrapper per sottoprocessi, che a sua volta mira a sostituire i moduli e le funzioni precedenti. Envoy è un sottoprocesso per l'uomo.

Esempio di utilizzo da README :

>>> r = envoy.run('git config', data='data to pipe in', timeout=2)

>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''

Fai pipa anche intorno:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]


36

os.systemè OK, ma un po 'datato. Inoltre non è molto sicuro. Invece, prova subprocess. subprocessnon chiama sh direttamente ed è quindi più sicuro di os.system.

Ottieni maggiori informazioni qui .


2
Pur condividendo la raccomandazione generale, subprocessnon rimuove tutti i problemi di sicurezza e presenta alcuni fastidiosi problemi.
Tripleee

36

Chiamare un comando esterno in Python

Semplice, utilizzare subprocess.run, che restituisce un CompletedProcessoggetto:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

Perché?

A partire da Python 3.5, la documentazione consiglia subprocess.run :

L'approccio raccomandato per invocare i sottoprocessi è utilizzare la funzione run () per tutti i casi d'uso che può gestire. Per casi d'uso più avanzati, l'interfaccia Popen sottostante può essere utilizzata direttamente.

Ecco un esempio dell'utilizzo più semplice possibile - e fa esattamente come richiesto:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

runattende che il comando termini correttamente, quindi restituisce un CompletedProcessoggetto. Può invece sollevare TimeoutExpired(se gli dai un timeout=argomento) o CalledProcessError(se fallisce e passicheck=True ).

Come si può dedurre dall'esempio precedente, stdout e stderr vengono entrambi reindirizzati al proprio stdout e stderr per impostazione predefinita.

Possiamo ispezionare l'oggetto restituito e vedere il comando che è stato dato e il codice di ritorno:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

Catturare l'output

Se si desidera acquisire l'output, è possibile passare subprocess.PIPEall'appropriato stderro stdout:

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(Trovo interessante e leggermente controintuitivo che le informazioni sulla versione vengano messe su stderr invece che su stdout.)

Passa un elenco di comandi

Si potrebbe facilmente passare dal fornire manualmente una stringa di comando (come suggerisce la domanda) a fornire una stringa creata a livello di programmazione. Non creare stringhe a livello di codice. Questo è un potenziale problema di sicurezza. È meglio presumere che non ti fidi dell'input.

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

Nota, solo argsdovrebbe essere passato in posizione.

Firma completa

Ecco la firma effettiva nella fonte e come mostrato da help(run):

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

I popenargse kwargssono dati al Popencostruttore. inputpuò essere una stringa di byte (o unicode, se specifica la codifica o universal_newlines=True) che verrà reindirizzata allo stdin del sottoprocesso.

La documentazione descrive timeout=e check=Truemeglio di quanto potessi:

L'argomento timeout viene passato a Popen.communicate (). Se il timeout scade, il processo figlio verrà interrotto e atteso. L'eccezione TimeoutExpired verrà nuovamente sollevata al termine del processo figlio.

Se check è true e il processo termina con un codice di uscita diverso da zero, verrà sollevata un'eccezione CalledProcessError. Gli attributi di tale eccezione contengono gli argomenti, il codice di uscita e stdout e stderr se sono stati acquisiti.

e questo esempio per check=Trueè migliore di uno che potrei trovare:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Firma estesa

Ecco una firma estesa, come indicato nella documentazione:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

Si noti che ciò indica che solo l'elenco args deve essere passato in posizione. Quindi passa gli argomenti rimanenti come argomenti di parole chiave.

popen

Quando usare Popeninvece? Farei fatica a trovare un caso d'uso basato solo sugli argomenti. L'utilizzo diretto di Popen, tuttavia, ti darebbe accesso ai suoi metodi, tra cui poll"send_signal", "terminate" e "wait".

Ecco la Popenfirma come indicato nella fonte . Penso che questo sia l'incapsulamento più preciso delle informazioni (al contrario di help(Popen)):

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

Ma più istruttiva è la Popendocumentazione :

subprocess.Popen(args, bufsize=-1, executable=None, stdin=None,
                 stdout=None, stderr=None, preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

Eseguire un programma figlio in un nuovo processo. Su POSIX, la classe utilizza un comportamento simile a os.execvp () per eseguire il programma figlio. Su Windows, la classe utilizza la funzione CreateProcess () di Windows. Gli argomenti a Popen sono i seguenti.

La comprensione della documentazione rimanente Popenverrà lasciata come esercizio per il lettore.


Un semplice esempio di comunicazione bidirezionale tra un processo primario e un sottoprocesso può essere trovato qui: stackoverflow.com/a/52841475/1349673
James Hirschorn

Il primo esempio dovrebbe probabilmente avere shell=True(o meglio ancora) passare il comando come elenco.
Tripleee

33

C'è anche Plumbum

>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad()                                   # Notepad window pops up
u''                                             # Notepad window is closed by user, command returns

28

Uso:

import os

cmd = 'ls -al'

os.system(cmd)

os: questo modulo fornisce un modo portatile di utilizzare la funzionalità dipendente dal sistema operativo.

Per ulteriori osfunzioni, ecco la documentazione.


2
è anche deprecato. usa il sottoprocesso
Corey Goldberg

28

Può essere così semplice:

import os
cmd = "your command"
os.system(cmd)

1
Ciò non evidenzia gli svantaggi, che sono spiegati in maggior dettaglio nel PEP-324 . La documentazione per os.systemesplicitamente raccomanda di evitarlo a favore di subprocess.
Tripleee

25

Mi piace molto shell_command per la sua semplicità. È costruito sopra il modulo di sottoprocesso.

Ecco un esempio dalla documentazione:

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0

24

C'è un'altra differenza qui che non è stata menzionata in precedenza.

subprocess.Popenesegue il <comando> come sottoprocesso. Nel mio caso, devo eseguire il file <a> che deve comunicare con un altro programma, <b>.

Ho provato il sottoprocesso e l'esecuzione ha avuto esito positivo. Tuttavia <b> non è riuscito a comunicare con <a>. Tutto è normale quando corro entrambi dal terminale.

Ancora uno: (NOTA: kwrite si comporta in modo diverso dalle altre applicazioni. Se si prova quanto segue con Firefox, i risultati non saranno gli stessi.)

Se ci provi os.system("kwrite"), il flusso del programma si blocca fino a quando l'utente non chiude kwrite. Per ovviare a ciò ho provato invece os.system(konsole -e kwrite). Questo programma a tempo continuava a scorrere, ma kwrite divenne il sottoprocesso della console.

Chiunque esegue kwrite non è un sottoprocesso (cioè nel monitor di sistema deve apparire sul bordo più a sinistra dell'albero).


1
Cosa intendi con "Chiunque esegue kwrite non è un sottoprocesso" ?
Peter Mortensen,

23

os.systemnon consente di archiviare risultati, quindi se si desidera memorizzare i risultati in un elenco o qualcosa del genere, a subprocess.callfunziona.


22

subprocess.check_callè utile se non si desidera testare i valori restituiti. Genera un'eccezione su qualsiasi errore.


22

Tendo a usare il sottoprocesso insieme a shlex (per gestire l'escaping delle stringhe tra virgolette):

>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)

17

Spina senza vergogna, ho scritto una libreria per questo: P https://github.com/houqp/shell.py

È praticamente un involucro per popen e shlex per ora. Supporta anche i comandi di piping in modo da poter concatenare i comandi più facilmente in Python. Quindi puoi fare cose come:

ex('echo hello shell.py') | "awk '{print $2}'"

16

In Windows puoi semplicemente importare il subprocessmodulo ed eseguire comandi esterni chiamando subprocess.Popen(), subprocess.Popen().communicate()e subprocess.Popen().wait()come di seguito:

# Python script to run a command line
import subprocess

def execute(cmd):
    """
        Purpose  : To execute a command and return exit status
        Argument : cmd - command to execute
        Return   : exit_code
    """
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (result, error) = process.communicate()

    rc = process.wait()

    if rc != 0:
        print "Error: failed to execute command:", cmd
        print error
    return result
# def

command = "tasklist | grep python"
print "This process detail: \n", execute(command)

Produzione:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K

15

Sotto Linux, nel caso in cui desideri chiamare un comando esterno che verrà eseguito in modo indipendente (continuerà a funzionare dopo la fine dello script python), puoi utilizzare una semplice coda come spooler di attività o il comando at

Un esempio con lo spooler di attività:

import os
os.system('ts <your-command>')

Note sullo spooler di attività ( ts):

  1. È possibile impostare il numero di processi simultanei da eseguire ("slot") con:

    ts -S <number-of-slots>

  2. L'installazione tsnon richiede i privilegi di amministratore. Puoi scaricarlo e compilarlo dal sorgente con un semplice make, aggiungerlo al tuo percorso e il gioco è fatto.


1
tsnon è standard su nessuna distro che conosco, sebbene l'indicatore a atsia leggermente utile. Probabilmente dovresti anche menzionare batch. Come altrove, la os.system()raccomandazione dovrebbe probabilmente menzionare almeno subprocessla sostituzione consigliata.
Tripleee

15

È possibile utilizzare Popen, quindi è possibile verificare lo stato della procedura:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Dai un'occhiata a subprocess.Popen .


15

Per recuperare l'id di rete da OpenStack Neutron :

#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read()  /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)

Uscita dell'elenco di reti nova

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

Uscita di stampa (networkId)

27a74fcd-37c0-4789-9414-9531b7e3f126

Non dovresti raccomandarlo os.popen()nel 2016. Lo script Awk potrebbe essere facilmente sostituito con codice Python nativo.
Tripleee
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.