run - subprocess python ita




Chiamando un comando esterno in Python (20)

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?


Chiamando un comando esterno in Python

Semplice, usa subprocess.run , che restituisce un oggetto CompletedProcess :

>>> 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 raccomanda subprocess.run :

L'approccio consigliato 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 di Popen sottostante può essere utilizzata direttamente.

Ecco un esempio dell'uso 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)

run il completamento del comando, quindi restituisce un oggetto CompletedProcess . Può invece generare TimeoutExpired (se gli si TimeoutExpired un timeout= argomento) o CalledProcessError (se fallisce e si passa check=True ).

Come puoi 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 il subprocess.PIPE allo stderr o allo stdout appropriato:

>>> 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 inserite in stderr anziché in stdout).

Passare una lista di comandi

Si potrebbe facilmente passare dalla fornitura manuale di una stringa di comando (come suggerisce la domanda) a fornire una stringa costruita a livello di codice. 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 gli args dovrebbero essere passati in modo posizionale.

Firma completa

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

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

I popenargs e i popenargs sono dati al costruttore di Popen . input può essere una stringa di byte (o unicode, se specifichi encoding o universal_newlines=True ) che verrà reindirizzato allo stdin del sottoprocesso.

La documentazione descrive timeout= e check=True meglio di quanto potrei:

L'argomento di timeout viene passato a Popen.communicate (). Se il timeout scade, il processo figlio verrà ucciso e atteso. L'eccezione TimeoutExpired verrà controrilanciata al termine del processo secondario.

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

e questo esempio per check=True è meglio di uno che potrei inventare:

>>> 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 espansa

Ecco una firma espansa, 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 questo indica che solo l'elenco di argomenti deve essere passato in modo posizionale. Quindi passa gli argomenti rimanenti come argomenti per le parole chiave.

popen

Quando usi Popen invece? Farei fatica a trovare casi d'uso basati solo sugli argomenti. L'uso diretto di Popen , tuttavia, ti consentirà di accedere ai suoi metodi, inclusi poll , "send_signal", "terminate" e "wait".

Ecco la firma di Popen come indicata 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ù informativa è la documentazione di Popen :

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)

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

Capire la documentazione rimanente su Popen verrà lasciata come esercizio per il lettore.


Aggiornare:

subprocess.run è l'approccio consigliato da Python 3.5 se il tuo codice non ha bisogno di mantenere la compatibilità con le versioni precedenti di Python. È più coerente e offre una facilità d'uso simile a quella di Envoy. (Piping non è così semplice. Vedi questa domanda per come .)

Ecco alcuni esempi dai documenti .

Esegui un processo:

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

Rilancio su corsa fallita:

>>> 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 l'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 https://github.com/kennethreitz/envoy . È un wrapper per sottoprocesso, che a sua volta mira a sostituire i vecchi moduli e funzioni. Envoy è un sottoprocesso per gli umani.

Esempio di utilizzo dal 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
''

Pipe in giro anche:

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

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

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

È possibile utilizzare Popen e quindi è possibile controllare lo stato della procedura:

from subprocess import Popen

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

Scopri subprocess.Popen .


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 il flag di creazione processo DETACHED_PROCESS 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.


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

subprocess.Popen esegue il <comando> come sottoprocesso. Nel mio caso, ho bisogno di eseguire il file <a> che deve comunicare con un altro programma, <b>.

Ho provato il sottoprocesso e l'esecuzione è andata a buon fine. Tuttavia <b> non ha potuto comunicare con <a>. Tutto è normale quando eseguo entrambi dal terminale.

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

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

Chiunque esegua il kwrite non è un sottoprocesso (cioè nel monitor di sistema deve apparire al limite più a sinistra dell'albero).


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: c'è anche un sottoprocesso.popen). os eseguirà sempre la shell ed è una semplice alternativa per le persone che non ne hanno bisogno, o 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 [email protected]:.') # 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". Viene utilizzato come wrapper di convenienza attorno al modulo di subprocess .

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 poiché il subprocess è un'alternativa migliore.

La modifica era basata sul commento di JF Sebastian.


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

Ecco come eseguo i miei comandi. Questo codice ha tutto ciò che ti serve più o meno

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

Guarda il modulo subprocess nella libreria standard:

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

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

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

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

La sezione " Sostituzione delle precedenti funzionalità con il modulo del sottoprocesso " nella documentazione del sottoprocesso potrebbe contenere alcune utili ricette.

Documentazione ufficiale sul modulo subprocess :


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 i dati di stdout nella pipe. In effetti, puoi semplicemente omettere quei parametri ( stdout= e stderr= ) e si comporterà come os.system() .


Plug spudorato, ho scritto una libreria per questo: P https://github.com/houqp/shell.py

Per ora è fondamentalmente un wrapper per popen e shlex. Supporta anche i comandi di piping in modo da poterli ordinare più facilmente in Python. Quindi puoi fare cose come:

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

Può essere così semplice:

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

Senza l'output del risultato:

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

Con l'output del risultato:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

Tendo ad 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)


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

Ottieni maggiori informazioni here .


subprocess.check_call è conveniente se non si desidera testare i valori di ritorno. Genera un'eccezione su qualsiasi errore.


Per recuperare l'ID di rete dal neutrone di openstack:

#!/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 di nova net-list

+--------------------------------------+------------+------+
| 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 |
+--------------------------------------+------------+------+

Output of print (networkId)

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

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

Un esempio con lo spooler di attività:

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

Note su task spooler ( ts):

  1. È possibile impostare il numero di processi concorrenti 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.


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 subprocess , che altre risposte hanno trattato bene.





external