Domanda Chiamando un comando esterno in Python


Come posso chiamare un comando esterno (come se l'avessi inserito nella shell Unix o nel prompt dei comandi di Windows) da uno script Python?


3622
2017-09-18 01:35


origine


risposte:


Guarda al modulo sottoprocesso nella libreria standard:

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

Il vantaggio di sottoprocesso vs. sistema è che è più flessibile (puoi ottenere lo stdout, lo stderr, il codice di stato "reale", una migliore gestione degli errori, ecc ...).

Il documentazione ufficiale raccomanda il sottoprocesso modulo sull'alternativa os.system ():

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

Il "Sostituzione delle funzioni precedenti con il modulo del sottoprocesso"sezione in sottoprocesso la documentazione può avere alcune ricette utili.

Documentazione ufficiale sul sottoprocesso modulo:


3490
2017-09-18 01:39



Ecco una sintesi dei modi per chiamare i 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 tuo sistema. Questo è bello perché puoi eseguire più comandi contemporaneamente in questo modo e configurare pipe e reindirizzamenti input / output. Per esempio:

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

    Tuttavia, mentre questo è conveniente, devi gestire manualmente l'escape dei caratteri della shell come spazi, ecc. D'altra parte, questo ti permette anche di eseguire comandi che sono semplicemente comandi di shell e non programmi effettivamente esterni. Vedere la documentazione.

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

  3. Il Popen classe del subprocess modulo. Questo è inteso come un sostituto per os.popen ma ha il rovescio della medaglia di essere leggermente più complicato in virtù di essere così completo. Ad esempio, potresti dire:

    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.

  4. Il call funzione dal subprocess modulo. Questo è fondamentalmente proprio come il Popen classe e prende tutti gli stessi argomenti, ma semplicemente attende 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.

  5. Se sei su Python 3.5 o successivo, puoi usare il nuovo subprocess.run funzione, che è molto simile a quanto sopra ma ancora più flessibile e restituisce a CompletedProcess oggetto quando il comando termina l'esecuzione.

  6. Il modulo os ha anche tutte le funzioni fork / exec / spawn che avresti in un programma C, ma non è consigliabile utilizzarle direttamente.

Il subprocess il modulo dovrebbe probabilmente essere quello che usi.

Infine, si prega di essere consapevoli del fatto che per tutti i metodi in cui si passa il comando finale per essere eseguito dalla shell come una stringa e si è responsabili per la sua evasione. Ci sono serie implicazioni sulla sicurezza se una qualsiasi parte della stringa che passi non può essere completamente attendibile. Ad esempio, se un utente sta inserendo parte / parte della stringa. Se non sei sicuro, usa solo questi metodi con le costanti. Per darti un suggerimento sulle implicazioni, considera questo codice:

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

e immagina che l'utente inserisca "mia mamma non mi amava && rm -rf /".


2466
2017-09-18 13:11



Io di solito uso:

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 ciò che vuoi con il stdout dati nella pipa. In effetti, puoi semplicemente omettere quei parametri (stdout= e stderr=) e si comporterà come os.system().


255
2017-09-18 18:20



Alcuni suggerimenti sullo scollegamento del processo figlio da quello chiamante (avvio del processo figlio in background).

Supponiamo di voler avviare una lunga attività da uno script CGI, ovvero che il processo figlio debba durare più a lungo del processo di esecuzione dello script CGI.

L'esempio classico dei documenti del modulo subprocess è:

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 vuole attendere nella riga 'chiamata sottoprocesso' fino a quando longtask.py non è finito. Ma non è chiaro cosa succede dopo la riga 'un po' di codice qui 'dell'esempio.

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

Su windows (win xp), il processo genitore non finirà fino a quando longtask.py avrà terminato il suo lavoro. Non è quello che vuoi in CGI-script. Il problema non è specifico per Python, nella comunità PHP i problemi sono gli stessi.

La soluzione è passare DETACHED_PROCESS Flag di creazione processo alla funzione CreateProcess sottostante in win API. 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 note, che il flag semanticamente corretto è CREATE_NEW_CONSOLE (0x00000010) * /

Su freebsd abbiamo un altro problema: quando il processo genitore è finito, finisce anche i processi figli. E neanche quello che vuoi in CGI-script. Alcuni esperimenti hanno mostrato 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 le ragioni del comportamento su freebsd. Se qualcuno lo sa, per favore condividi le tue idee. Googling sull'avvio di processi in background in Python non fa ancora luce.


157
2018-02-12 10:15



Consiglierei di usare il modulo di sottoprocesso invece di os.system perché fa scappare l'escape per te ed è quindi molto più sicuro: 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)

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


93
2017-09-18 01:37



import os
os.system("your command")

Si noti che questo è pericoloso, dal momento che il comando non è pulito. Lascio a te il compito di google per la documentazione pertinente sui moduli 'os' e 'sys'. Ci sono un sacco di funzioni (exec * e spawn *) che faranno cose simili.


82
2017-09-18 01:37



Io uso sempre fabric per questo 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 subprocess Python).

Guarda un esempio:

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

51
2018-03-13 00:12



Controlla anche la libreria Python "pexpect".

Permette 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')

51
2017-10-07 07:09



Ci sono molte librerie diverse che ti permettono 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 tutte le librerie che ho elencato e collegato la documentazione per ognuna di esse.

fonti:

Queste sono tutte le librerie:

Speriamo che questo ti aiuterà a prendere una decisione su quale libreria usare :)

sottoprocesso

Il sottoprocesso consente di chiamare comandi esterni e collegarli alle loro pipe di input / output / error (stdin, stdout e stderr). Il sottoprocesso è la scelta predefinita per i comandi in esecuzione, 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.system e os.popen (Nota: esiste anche un sottoprocesso.popen). os eseguirà sempre la shell ed è una semplice alternativa per le persone che non ne hanno bisogno o che non sanno come usare 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. Questo è 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 "script-like". Puoi chiamare programmi come funzioni come in sh. Plumbum è utile se vuoi eseguire una pipeline senza la shell.

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

pexpect

pexpect ti permette di generare applicazioni figlio, controllarle e trovare pattern nel loro output. Questa è un'alternativa migliore ai sottoprocessi per i comandi che si aspettano una 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. Permette di eseguire comandi shell locali e remoti. Fabric è un'alternativa semplice per l'esecuzione di 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

l'inviato è noto come "sottoprocesso per gli umani". È usato come involucro di convenienza intorno al subprocess modulo.

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

comandi

commands contiene funzioni wrapper per os.popen, ma è stato rimosso da Python 3 da allora subprocess è un'alternativa migliore.

La modifica era basata sul commento di J.F. Sebastian.


46
2017-10-29 14:02



Se hai bisogno dell'output dal comando che stai chiamando, allora 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'

Si noti anche il conchiglia parametro.

Se la shell è True, il comando specificato verrà eseguito tramite la shell. Questo 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 comunque un comodo accesso ad altre funzionalità della shell quali shell shell, caratteri jolly del nome del file, espansione della variabile di ambiente ed espansione di ~ alla home di un utente directory. Tuttavia, si noti che Python stesso offre implementazioni di molte funzionalità simili a shell (in particolare, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), e shutil).


45
2018-04-28 20:29