Come ottenere l'output da un comando esterno combinare con Pipe
Domanda
Ho un comando come questo.
wmctrl -lp | awk '/gedit/ { print $1 }'
E voglio il suo output all'interno dello script Python, ho provato questo codice
>>> import subprocess
>>> proc = subprocess.Popen(["wmctrl -lp", "|","awk '/gedit/ {print $1}"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> proc.stdout.readline()
'0x0160001b -1 6504 beer-laptop x-nautilus-desktop\n'
>>> proc.stdout.readline()
'0x0352f117 0 6963 beer-laptop How to get output from external command combine with Pipe - Stack Overflow - Chromium\n'
>>> proc.stdout.readline()
'0x01400003 -1 6503 beer-laptop Bottom Expanded Edge Panel\n'
>>>
Sembra che il mio codice sia sbagliato solo wmctrl -lp
è stato eseguito e | awk "{print $ 1}"
è stato omesso
Il mio risultato atteso sarebbe 0x03800081
$ wmctrl -lp | awk '/gedit/ {print $1}'
0x03800081
Per favore, ti aiuta.
Soluzione
Con shell = True
, è necessario utilizzare una singola riga di comando anziché un array, altrimenti gli argomenti aggiuntivi vengono interpretati come argomenti di shell. Dalla sottoprocesso
documentazione :
Su Unix, con shell = True: se args è una stringa, specifica la stringa di comando da eseguire attraverso la shell. Se args è una sequenza, il primo elemento specifica la stringa di comando e tutti gli elementi aggiuntivi verranno trattati come argomenti di shell aggiuntivi.
Quindi la tua chiamata dovrebbe essere:
subprocess.Popen("wmctrl -lp | sed /gedit/ '{print $1}'", shell=True, ...
Penso che potresti anche avere una sola citazione non bilanciata.
Altri suggerimenti
Poiché stai passando una sequenza per il programma, pensa che la pipe sia un argomento in wmcrtrl
, come se lo avessi fatto
wmctrl -lp "|"
e quindi l'operazione pipe effettiva viene persa.
Renderlo una singola stringa dovrebbe davvero darti il ??risultato corretto:
>>> import subprocess as s
>>> proc = s.Popen("echo hello | grep e", shell=True, stdout=s.PIPE, stderr=s.PIPE)
>>> proc.stdout.readline()
'hello\n'
>>> proc.stdout.readline()
''
Dopo alcune ricerche, ho il seguente codice che funziona molto bene per me. Fondamentalmente stampa sia stdout che stderr in tempo reale. Spero che aiuti qualcun altro che ne abbia bisogno.
stdout_result = 1
stderr_result = 1
def stdout_thread(pipe):
global stdout_result
while True:
out = pipe.stdout.read(1)
stdout_result = pipe.poll()
if out == '' and stdout_result is not None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
def stderr_thread(pipe):
global stderr_result
while True:
err = pipe.stderr.read(1)
stderr_result = pipe.poll()
if err == '' and stderr_result is not None:
break
if err != '':
sys.stdout.write(err)
sys.stdout.flush()
def exec_command(command, cwd=None):
if cwd is not None:
print '[' + ' '.join(command) + '] in ' + cwd
else:
print '[' + ' '.join(command) + ']'
p = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
)
out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))
err_thread.start()
out_thread.start()
out_thread.join()
err_thread.join()
return stdout_result + stderr_result
Se necessario, penso che sia facile raccogliere l'output o l'errore in una stringa e restituirlo.