run Chiamando un comando esterno in Python




subprocess python ita (24)

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.

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?


os.system non ti permette di memorizzare i risultati, quindi se vuoi archiviare i risultati in qualche lista o qualcosa di subprocess.call funziona.


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

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

Può essere così semplice:

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

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.


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

Ecco un esempio dei documenti:

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

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}'"

Io uso sempre il fabric per 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)

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 .


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 :


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

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.


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.


È 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 .


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



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 shell è True , il comando specificato verrà eseguito attraverso 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 ).


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. Vedi la documentazione .

  2. stream = os.popen("some_command with args") farà la stessa cosa di os.system tranne per il fatto 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. Vedi la documentazione .

  3. La classe Popen del modulo subprocess . Questo è inteso come un sostituto di 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. Vedi la documentazione .

  4. La funzione di call dal modulo subprocess . Questo è fondamentalmente simile alla classe Popen e prende tutti gli stessi argomenti, ma semplicemente attende il completamento del comando e fornisce il codice di ritorno. Per esempio:

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

    Vedi la documentazione .

  5. Se si utilizza Python 3.5 o versioni successive, è possibile utilizzare la nuova funzione subprocess.run , che è molto simile alla precedente ma anche più flessibile e restituisce un oggetto CompletedProcess 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 modulo di subprocess 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 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 /".


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)

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

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

Uso:

import os

cmd = 'ls -al'

os.system(cmd)

os - Questo modulo fornisce un modo portatile per utilizzare la funzionalità dipendente dal sistema operativo.

Per le funzioni più os , here la documentazione.


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




external