Esecuzione di comandi Bash in Python


299

Sul mio computer locale, eseguo uno script Python che contiene questa riga

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)

Funziona benissimo.

Quindi eseguo lo stesso codice su un server e ricevo il seguente messaggio di errore

'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import  diag
ImportError: No module named swap

Quindi quello che ho fatto è stato inserire una print bashCommandche mi stampa del comando nel terminale prima di eseguirlo os.system().

Certo, ricevo di nuovo l'errore (causato da os.system(bashCommand)) ma prima di quell'errore stampa il comando nel terminale. Quindi ho appena copiato quell'output e ho fatto una copia incolla nel terminale e ho premuto invio e funziona ...

Qualcuno ha la minima idea di cosa stia succedendo?


2
Sembra che ci sia una differenza nell'ambiente a seconda di come si esegue cwm. Forse hai qualche configurazione .bashrcche imposta l'ambiente per l'uso bash interattivo?
Sven Marnach,

Hai provato a eseguire il comando dalla riga di comando quando hai effettuato l'accesso sul server? Il tuo post dice solo che "lo hai incollato nel terminale".
Sven Marnach,

@Sven: sì, intendevo dire che ho eseguito il comando direttamente nel terminale del server
mkn

Sembra che ci sia una differenza nel PYTHONPATH a seconda di come corri cwm. O forse c'è una differenza nel PERCORSO e cwmvengono chiamate versioni diverse di . O diverse versioni di Python. È davvero difficile capirlo senza accesso alla macchina ...
Sven Marnach,

Risposte:


314

Non usare os.system. È stato deprecato a favore del sottoprocesso . Dai documenti : "Questo modulo si propone di sostituire diverse anziani moduli e funzioni: os.system, os.spawn".

Come nel tuo caso:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

8
Questo non ha fatto quello che volevo quando dovevo fare un cd 'path\to\somewhere'seguito da un altro comando bash che doveva essere eseguito da qualche parte. @ user225312
AWrightIV

36
@AWrightIV Se hai bisogno che il tuo sottoprocesso venga eseguito in una particolare directory di lavoro, puoi usare l' cwdargomento Popen:subprocess.Popen(..., cwd='path\to\somewhere')
waterproof

7
Per il mio comando avevo bisogno di shell = True come qui; stackoverflow.com/questions/18962785/...
user984003

4
È meglio usare shlex.split () invece string.split () in questo caso
Alexey Sviridov

4
... ( stdout=filereindirizza l'output su un file in questo caso. Implementa > file). Sarebbe sbagliato passare ..., '>', 'file']l'ultimo comando in attesa del reindirizzamento (non funzionerà senza una shell e se usi una shell, dovresti passare il comando come stringa)
jfs

186

Per espandere un po 'le risposte precedenti qui, ci sono una serie di dettagli che sono comunemente trascurati.

  • Preferisci subprocess.run()over subprocess.check_call()e amici over subprocess.call()over subprocess.Popen()over os.system()overos.popen()
  • Comprendi e probabilmente usa text=True, aka universal_newlines=True.
  • Comprendi il significato shell=Trueo il shell=Falsemodo in cui cambia la quotazione e la disponibilità delle comodità della shell.
  • Comprendi le differenze tra she Bash
  • Comprendi come un sottoprocesso è separato dal suo genitore e generalmente non può cambiarlo.
  • Evitare di eseguire l'interprete Python come sottoprocesso di Python.

Questi argomenti sono trattati più dettagliatamente di seguito.

Prefer subprocess.run()osubprocess.check_call()

La subprocess.Popen()funzione è un cavallo di lavoro di basso livello ma è difficile da usare correttamente e finisci per copiare / incollare più righe di codice ... che già esistono convenientemente nella libreria standard come un insieme di funzioni wrapper di livello superiore per vari scopi, che sono presentati in modo più dettagliato di seguito.

Ecco un paragrafo dalla documentazione :

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

Sfortunatamente, la disponibilità di queste funzioni wrapper differisce tra le versioni di Python.

  • subprocess.run()è stato ufficialmente introdotto in Python 3.5. È destinato a sostituire tutto quanto segue.
  • subprocess.check_output()è stato introdotto in Python 2.7 / 3.1. È sostanzialmente equivalente asubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
  • subprocess.check_call()è stato introdotto in Python 2.5. È sostanzialmente equivalente asubprocess.run(..., check=True)
  • subprocess.call()è stato introdotto in Python 2.4 nel subprocessmodulo originale ( PEP-324 ). È sostanzialmente equivalente asubprocess.run(...).returncode

API di alto livello vs subprocess.Popen()

Il refactored ed esteso subprocess.run()è più logico e più versatile rispetto alle precedenti funzioni legacy che sostituisce. Restituisce un CompletedProcessoggetto che ha vari metodi che consentono di recuperare lo stato di uscita, l'output standard e alcuni altri risultati e indicatori di stato dal sottoprocesso finito.

subprocess.run()è la strada da percorrere se hai semplicemente bisogno di un programma per eseguire e restituire il controllo a Python. Per scenari più coinvolti (processi in background, forse con I / O interattivo con il programma padre Python) è comunque necessario utilizzare subprocess.Popen()e prendersi cura di tutto l'impianto idraulico. Ciò richiede una comprensione abbastanza complessa di tutte le parti mobili e non dovrebbe essere intrapreso alla leggera. L' Popenoggetto più semplice rappresenta il processo (possibilmente ancora in esecuzione) che deve essere gestito dal codice per il resto della durata del sottoprocesso.

Va forse sottolineato che subprocess.Popen()crea semplicemente un processo. Se lo lasci a questo, hai un sottoprocesso in esecuzione contemporaneamente a Python, quindi un processo "in background". Se non è necessario eseguire input o output o coordinarsi in altro modo con te, può fare un utile lavoro in parallelo con il tuo programma Python.

Evita os.system()eos.popen()

Da tempo eterno (o meglio, dal momento che Python 2.5) la osdocumentazione del modulo è contenuta la raccomandazione di preferire subprocesssopra os.system():

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

Il problema system()è che dipende ovviamente dal sistema e non offre modi per interagire con il sottoprocesso. Funziona semplicemente, con output standard ed errore standard fuori dalla portata di Python. Le uniche informazioni che Python riceve indietro sono lo stato di uscita del comando (zero significa successo, anche se il significato di valori diversi da zero dipende anche dal sistema).

PEP-324 (che è già stato menzionato sopra) contiene una logica più dettagliata del perché os.systemè problematico e di come i subprocesstentativi di risolvere questi problemi.

os.popen()era ancora più fortemente scoraggiato :

Obsoleto dalla versione 2.6: questa funzione è obsoleta. Usa il subprocessmodulo.

Tuttavia, da qualche tempo in Python 3, è stato reimplementato per essere semplicemente utilizzato subprocesse reindirizza alla subprocess.Popen()documentazione per i dettagli.

Comprendi e usa di solito check=True

Noterai anche che subprocess.call()ha molte delle stesse limitazioni di os.system(). In uso regolare, si dovrebbe verificare se in generale il processo terminato con successo, che subprocess.check_call()e subprocess.check_output()fare (se quest'ultimo restituisce anche l'uscita standard del sottoprocesso finito). Allo stesso modo, è consigliabile usarlo check=Truecon a subprocess.run()meno che non sia necessario consentire specificamente al sottoprocesso di restituire uno stato di errore.

In pratica, con check=Trueo subprocess.check_*, Python genererà CalledProcessErrorun'eccezione se il sottoprocesso restituisce uno stato di uscita diverso da zero.

Un errore comune subprocess.run()è quello di omettere check=Trueed essere sorpreso quando il codice a valle fallisce se il sottoprocesso fallisce.

D'altra parte, un problema comune con check_call()ed check_output()era che gli utenti che usavano ciecamente queste funzioni erano sorpresi quando veniva sollevata l'eccezione, ad esempio quando grepnon trovavano una corrispondenza. (Probabilmente dovresti sostituire comunque grepcon il codice nativo Python, come indicato di seguito.)

Tutto sommato, è necessario capire come i comandi della shell restituiscono un codice di uscita e in quali condizioni restituiranno un codice di uscita diverso da zero (errore) e prendere una decisione consapevole su come deve essere gestito esattamente.

Comprendi e probabilmente usa text=Trueakauniversal_newlines=True

Da Python 3, le stringhe interne a Python sono stringhe Unicode. Ma non vi è alcuna garanzia che un sottoprocesso generi output Unicode o stringhe.

(Se le differenze non sono immediatamente evidenti, si consiglia la Pragmatic Unicode di Ned Batchelder , se non addirittura obbligatoria, la lettura. Se preferisci, c'è una presentazione video di 36 minuti dietro il link, anche se probabilmente leggere la pagina da solo richiederà molto meno tempo. )

In fondo, Python deve recuperare un bytesbuffer e interpretarlo in qualche modo. Se contiene un BLOB di dati binari, non dovrebbe essere decodificato in una stringa Unicode, perché è un comportamento soggetto a errori e che causa errori - proprio il tipo di comportamento fastidioso che ha crivellato molti script di Python 2, prima che ci fosse un modo per distinguere correttamente tra testo codificato e dati binari.

Con text=True, dici a Python che, in effetti, ti aspetti dati testuali nella codifica predefinita del sistema e che dovrebbe essere decodificato in una stringa Python (Unicode) al meglio delle capacità di Python (di solito UTF-8 su qualsiasi moderatamente fino a sistema di data, tranne forse Windows?)

In caso contrario , Python ti fornirà solo bytesstringhe nelle stringhe stdoute stderr. Forse a un certo punto si successivamente fai a sapere che erano le stringhe di testo, dopo tutto, e si conosce il loro codifica. Quindi, puoi decodificarli.

normal = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True,
    text=True)
print(normal.stdout)

convoluted = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))

Python 3.7 ha introdotto l'alias più breve, più descrittivo e comprensibile textper l'argomento della parola chiave che in precedenza era stato chiamato in qualche modo fuorviante universal_newlines.

Capire shell=Truevsshell=False

Con shell=Truete passa una singola stringa alla tua shell e la shell la prende da lì.

Con shell=Falsete passa un elenco di argomenti al sistema operativo, bypassando la shell.

Quando non si dispone di una shell, si salva un processo e si elimina una notevole quantità di complessità nascosta, che può o meno contenere bug o persino problemi di sicurezza.

D'altra parte, quando non si dispone di una shell, non si dispone di reindirizzamento, espansione di caratteri jolly, controllo dei processi e un gran numero di altre funzionalità della shell.

Un errore comune è usare shell=Truee poi passare a Python un elenco di token, o viceversa. Ciò accade in alcuni casi, ma è davvero mal definito e potrebbe rompersi in modi interessanti.

# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')

# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    shell=True)

# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
    shell=True)

correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    # Probably don't forget these, too
    check=True, text=True)

# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
    shell=True,
    # Probably don't forget these, too
    check=True, text=True)

La replica comune "ma funziona per me" non è una confutazione utile a meno che tu non capisca esattamente in quali circostanze potrebbe smettere di funzionare.

Esempio di refactoring

Molto spesso, le funzionalità della shell possono essere sostituite con codice Python nativo. Gli Awk o gli sedscript semplici dovrebbero probabilmente essere semplicemente tradotti in Python.

Per illustrare parzialmente questo, ecco un esempio tipico ma leggermente sciocco che coinvolge molte caratteristiche della shell.

cmd = '''while read -r x;
   do ping -c 3 "$x" | grep 'round-trip min/avg/max'
   done <hosts.txt'''

# Trivial but horrible
results = subprocess.run(
    cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)

# Reimplement with shell=False
with open('hosts.txt') as hosts:
    for host in hosts:
        host = host.rstrip('\n')  # drop newline
        ping = subprocess.run(
             ['ping', '-c', '3', host],
             text=True,
             stdout=subprocess.PIPE,
             check=True)
        for line in ping.stdout.split('\n'):
             if 'round-trip min/avg/max' in line:
                 print('{}: {}'.format(host, line))

Alcune cose da notare qui:

  • Con shell=Falsete non hai bisogno del preventivo che la shell richiede intorno alle stringhe. Mettere comunque le virgolette è probabilmente un errore.
  • Spesso ha senso eseguire il minor codice possibile in un sottoprocesso. Questo ti dà un maggiore controllo sull'esecuzione dal tuo codice Python.
  • Detto questo, le complesse pipeline di shell sono noiose e talvolta difficili da reimplementare in Python.

Il codice refactored illustra anche quanto la shell fa davvero per te con una sintassi molto concisa, nel bene e nel male. Python afferma che esplicito è meglio di implicito, ma il codice Python è piuttosto dettagliato e probabilmente sembra più complesso di quanto non sia in realtà. D'altra parte, offre una serie di punti in cui è possibile ottenere il controllo nel mezzo di qualcos'altro, come banalmente esemplificato dal miglioramento che possiamo facilmente includere il nome host insieme all'output del comando shell. (Questo non è affatto impegnativo da fare nella shell, ma a spese di un altro diversivo e forse di un altro processo.)

Costrutti Shell comuni

Per completezza, ecco alcune brevi spiegazioni di alcune di queste funzionalità della shell e alcune note su come possono essere sostituite con le funzionalità native di Python.

  • L'espansione jolly Globbing aka può essere sostituita glob.glob()o molto spesso con semplici confronti di stringhe Python come for file in os.listdir('.'): if not file.endswith('.png'): continue. Bash ha varie altre strutture di espansione come l' .{png,jpg}espansione del rinforzo e {1..100}l'espansione tilde (si ~espande nella directory principale e più in generale ~accountnella directory principale di un altro utente)
  • Le variabili shell come $SHELLo $my_exported_vartalvolta possono essere semplicemente sostituite con variabili Python. Variabili di shell esportati sono disponibili come ad esempio os.environ['SHELL'](il significato exportè quello di rendere la variabile disposizione sottoprocessi -. Una variabile che non è disponibile per sottoprocessi ovviamente non essere disponibili per Python esecuzione come sottoprocesso del guscio, o viceversa La env=parola l'argomento ai subprocessmetodi consente di definire l'ambiente del sottoprocesso come dizionario, quindi è un modo per rendere visibile una variabile Python a un sottoprocesso). Con shell=Falsete dovrai capire come rimuovere eventuali preventivi; per esempio, cd "$HOME"equivale a os.chdir(os.environ['HOME'])senza virgolette attorno al nome della directory. (Molto spessocdnon è utile o necessario comunque, e molti principianti omettono le doppie virgolette attorno alla variabile e se ne vanno via fino a un giorno ... )
  • Il reindirizzamento ti consente di leggere da un file come input standard e di scrivere l'output standard su un file. grep 'foo' <inputfile >outputfilesi apre outputfileper la scrittura e inputfileper la lettura e passa i suoi contenuti come input standard a grep, il cui output standard arriva quindi outputfile. Questo non è generalmente difficile da sostituire con codice Python nativo.
  • Le pipeline sono una forma di reindirizzamento. echo foo | nlesegue due sottoprocessi, in cui l'output standard di echoè l'input standard di nl(a livello di sistema operativo, in sistemi simili a Unix, si tratta di un handle di file singolo). Se non è possibile sostituire una o entrambe le estremità della pipeline con il codice Python nativo, forse pensare all'utilizzo di una shell dopo tutto, soprattutto se la pipeline ha più di due o tre processi (sebbene si veda il pipesmodulo nella libreria standard di Python o un numero di concorrenti di terze parti più moderni e versatili).
  • Il controllo dei lavori consente di interrompere i lavori, eseguirli in background, riportarli in primo piano, ecc. I segnali Unix di base per interrompere e continuare un processo sono ovviamente disponibili anche da Python. Ma i lavori sono un'astrazione di livello superiore nella shell che coinvolge gruppi di processi ecc. Che devi capire se vuoi fare qualcosa del genere da Python.
  • Citando nella shell è potenzialmente confuso fino a quando non si capisce che tutto è fondamentalmente una stringa. Quindi ls -l /è equivalente a 'ls' '-l' '/'ma la citazione intorno ai letterali è completamente facoltativa. Le stringhe non quotate che contengono metacaratteri della shell subiscono espansione dei parametri, tokenizzazione degli spazi bianchi ed espansione dei caratteri jolly; le virgolette doppie impediscono la tokenizzazione degli spazi bianchi e l'espansione dei caratteri jolly, ma consentono l'espansione dei parametri (sostituzione variabile, sostituzione comando ed elaborazione barra rovesciata). Questo è semplice in teoria, ma può essere sconcertante, specialmente quando ci sono diversi livelli di interpretazione (un comando remoto della shell, per esempio).

Comprendi le differenze tra she Bash

subprocessesegue i comandi della shell a /bin/shmeno che non sia richiesto diversamente (tranne ovviamente su Windows, dove utilizza il valore della COMSPECvariabile). Ciò significa che non sono disponibili varie funzionalità solo Bash come array, [[ecc .

Se è necessario utilizzare la sintassi solo Bash, è possibile passare il percorso alla shell come executable='/bin/bash'(dove ovviamente se Bash è installato altrove, è necessario regolare il percorso).

subprocess.run('''
    # This for loop syntax is Bash only
    for((i=1;i<=$#;i++)); do
        # Arrays are Bash-only
        array[i]+=123
    done''',
    shell=True, check=True,
    executable='/bin/bash')

A subprocessè separato dal suo genitore e non può cambiarlo

Un errore piuttosto comune sta facendo qualcosa del genere

subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True)  # Doesn't work

che a parte la mancanza di eleganza tradisce anche una fondamentale mancanza di comprensione della parte "sub" del nome "sottoprocesso".

Un processo figlio viene eseguito completamente separato da Python e, al termine, Python non ha idea di ciò che ha fatto (a parte gli indicatori vaghi che può dedurre dallo stato di uscita e dall'output dal processo figlio). Un bambino generalmente non può cambiare l'ambiente del genitore; non può impostare una variabile, cambiare la directory di lavoro o, in così tante parole, comunicare con il suo genitore senza la cooperazione del genitore.

La correzione immediata in questo caso particolare consiste nell'eseguire entrambi i comandi in un singolo sottoprocesso;

subprocess.run('foo=bar; echo "$foo"', shell=True)

sebbene ovviamente questo particolare caso d'uso non richieda affatto la shell. Ricorda, puoi manipolare l'ambiente del processo corrente (e quindi anche i suoi figli) tramite

os.environ['foo'] = 'bar'

o passare un'impostazione ambientale a un processo figlio con

subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})

(per non parlare dell'ovvio refactoring subprocess.run(['echo', 'bar']), ma ovviamente echoè un cattivo esempio di qualcosa da eseguire in un sottoprocesso).

Non eseguire Python da Python

Questo è un consiglio leggermente dubbio; ci sono certamente situazioni in cui ha senso o è addirittura un requisito assoluto per eseguire l'interprete Python come sottoprocesso da uno script Python. Ma molto spesso, l'approccio corretto è semplicemente importall'altro modulo Python nello script chiamante e chiamare direttamente le sue funzioni.

Se l'altro script Python è sotto il tuo controllo e non è un modulo, considera di trasformarlo in uno . (Questa risposta è già troppo lunga, quindi non approfondirò qui i dettagli.)

Se hai bisogno di parallelismo, puoi eseguire le funzioni Python in sottoprocessi con il multiprocessingmodulo. C'è anche threadingche esegue più attività in un singolo processo (che è più leggero e ti dà più controllo, ma anche più vincolato nel fatto che i thread all'interno di un processo sono strettamente accoppiati e associati a un singolo GIL .)


2
Per un'esposizione più dettagliata di come potresti evitare di chiamare Python come sottoprocesso, vedi questa risposta su una domanda tangenzialmente simile.
triplo

4
mi sorprende che ho dovuto inviare una nuova risposta a una domanda così basilare per mostrare come eseguire il comando dalla domanda in modo idiomatico. La tua risposta è lunga ma non vedo questo esempio. Non correlato: evitare il culto del carico. Se check_call () funziona nel tuo caso, usalo. Ho dovuto riparare un codice usato alla run()cieca. Mancare ha check=Truecausato un bug che sarebbe evitato se si usasse check_call - "check" è nel nome, non si può perdere - è il valore predefinito corretto: non ignorare gli errori in silenzio. Non ho letto più.
jfs

1
@jfs Grazie per il feedback, avevo in effetti intenzione di aggiungere una sezione su Bash vs shma mi hai battuto. Sto cercando di precisare i dettagli in modo sufficientemente dettagliato per aiutare i principianti per i quali queste insidie ​​non sono ovvie, quindi è un po 'lungimirante. Il tuo dovrebbe essere abbastanza sufficiente altrimenti; +1
tripleee

Ha stderr/stdout = subprocess.PIPEun sovraccarico di prestazioni superiore rispetto alle impostazioni predefinite?
Stringers

1
@Stringers Non ho testato, ma non vedo perché dovrebbe. Se colleghi quei tubi a qualcosa che fa un po 'di elaborazione, allora ovviamente quella elaborazione deve essere rispettata; ma non succede nel tubo stesso. L'impostazione predefinita è di non catturare affatto stdout o stderr, cioè qualsiasi cosa venga stampata è fuori dalla visibilità e dal controllo di Python, proprio come con os.system().
Tripleee

41

Chiamalo con sottoprocesso

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

L'errore che si sta verificando sembra essere perché non esiste un modulo di scambio sul server, è necessario installare lo scambio sul server ed eseguire nuovamente lo script


3
Il swapmodulo è ovviamente lì, perché l'esecuzione del comando dalla shell funziona.
Sven Marnach,

2
Non sul server, quando lo esegue sul server c'è un errore di importazione.
Jakob Bowyer,

@mkn: "Quindi ho appena copiato quell'output e ho fatto una copia incolla nel terminale e ho premuto Invio e funziona ..." - Hai provato questo sul server o sul tuo computer?
Sven Marnach,

Stai eseguendo questo su un computer autonomo bene ma non funziona quando lo esegui sul tuo server? O sei in grado di eseguirlo su un terminale server ma non sul server stesso
Jakob Bowyer,

1
è sbagliato Se non lo usi shell=True, dovresti usare un elenco per passare più argomenti, ad esempio, usa ['a', 'b', 'c']invece di 'a b c'. Sebbene una divisione ingenua non funzionerà a causa di > file(reindirizzamento della shell) nel comando. Maggiori dettagli
jfs

18

È possibile usare il programma bash, con il parametro -c per eseguire i comandi:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])

2
subprocess.check_output(bashCommand, shell=True)fa la stessa cosa. Se il tuo comando è una stringa statica, prova ad analizzarlo da solo in un elenco ed evita il shell=True; anche se in questo caso hai bisogno della shell per il reindirizzamento, altrimenti dovrai with open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
rifattarla

@tripleee note: /bin/sh(utilizzato dal sottoprocesso) non è necessariamente bash(non è possibile utilizzare i bashismi). Anche se uno potrebbe usare executable='/bin/bashse lo si desidera. Ecco un esempio di codice
jfs

2
è la prima risposta in cui il comando dovrebbe avviarsi correttamente (le risposte accettate e la 2a popolare sono semplicemente sbagliate. Un piccolo cavillo: check_output()qui è inutile (l'output è sempre vuoto a causa del > filereindirizzamento; utilizzare check_call()invece.
jfs

16

Puoi usarlo subprocess, ma ho sempre pensato che non fosse un modo 'Pythonic' di farlo. Così ho creato Sultan (plug shameless) che semplifica l'esecuzione delle funzioni della riga di comando.

https://github.com/aeroxis/sultan


3
Molto bene! Molto più pulito e intuitivo dei sottoprocessi.
mjd2,

Grazie mille! Sono contento di sentirlo!
David Daniel,

2
Questo dovrebbe essere onestamente adottato nella libreria standard.
Joshua Detwiler,

1
C'è un modo per catturare l'output dal terminale usando Sultan?
alvas,

Sì, puoi @alvas ... Ecco i documenti su come farlo: sultan.readthedocs.io/en/latest/…
David Daniel,

7

In base all'errore manca un pacchetto denominato swap sul server. Questo lo /usr/bin/cwmrichiede. Se sei su Ubuntu / Debian, installa python-swapusando aptitude.


ma funziona quando lo eseguo direttamente nel terminale ... quindi lo swap deve essere lì, no?
MK

ci sono due opzioni. o non riesce a trovarlo swapo non avrebbe dovuto importarlo in primo luogo. puoi import swapmanualmente? funziona?
kichik,

hm non posso. Se avvio Python digitando Python nel terminale e quindi digito import swap, viene visualizzato l'errore "ImportError: nessun modulo chiamato swap". La cosa strana è ancora che funziona quando eseguo il comando cwm direttamente nel terminale del server
MK

Prova a stampare sys.pathdove funziona e dove non lo è. Quindi prova a cercare la cartella di scambio o swap.py nelle cartelle stampate. Come ha detto Sven, potrebbe esserci un problema con questi percorsi e questo ti aiuterà a capirlo.
Kichik,

4

Inoltre puoi usare 'os.popen'. Esempio:

import os

command = os.popen('ls -al')
print(command.read())
print(command.close())

Produzione:

total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root   77 ago 13 21:53 test.py

None

1
La documentazione contiene una grande scatola rossa: " Obsoleto dalla versione 2.6: questa funzione è obsoleta. Usa il subprocessmodulo."
tripleee,

1
In tutta onestà, os.popennon ha più questo avviso, ed è semplicemente un involucro sottile in giro subprocess.Popen()ora.
triplo

4

Per eseguire il comando senza shell, passare il comando come elenco e implementare il reindirizzamento in Python usando [subprocess]:

#!/usr/bin/env python
import subprocess

with open('test.nt', 'wb', 0) as file:
    subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
                          stdout=file)

Nota: no > test.ntalla fine. stdout=fileimplementa il reindirizzamento.


Per eseguire il comando utilizzando la shell in Python, passare il comando come stringa e abilitare shell=True:

#!/usr/bin/env python
import subprocess

subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
                      shell=True)

Qui la shell è responsabile del reindirizzamento dell'output ( > test.ntè nel comando).


Per eseguire un comando bash che utilizza bashisms, specificare l'eseguibile bash in modo esplicito, ad esempio, per emulare la sostituzione del processo bash :

#!/usr/bin/env python
import subprocess

subprocess.check_call('program <(command) <(another-command)',
                      shell=True, executable='/bin/bash')

Forse menzionare che .split()non è adeguato quando ci sono stringhe tra virgolette ecc. C'è una routine separata shlex.split()che affronta una sintassi della shell arbitrariamente complessa.
Tripleee

@tripleee .split()funziona in questo caso. shlex.split()può essere utile a volte ma può anche fallire in alcuni casi. Ci sono molte cose che potrebbero essere menzionate. È possibile iniziare con il collegamento alla descrizione del tag di sottoprocesso fornita sopra.
jfs,

0

Il modo pitone di farlo è usare subprocess.Popen

subprocess.Popen prende un elenco in cui il primo elemento è il comando da eseguire seguito da tutti gli argomenti della riga di comando.

Come esempio:

import subprocess

args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line

args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line

No, l'ultimo esempio è lo stesso di correre echo -v '"Hello Again!"'con virgolette singole attorno alle virgolette doppie.
Tripleee,

Inoltre, per usare correttamente subprocesss.Popen, devi gestire l'oggetto processo risultante (almeno, esegui a wait()per evitare che si trasformi in un processo zombi).
triplo
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.