Comment obtenir la sortie d'une commande externe combinée avec Pipe
Question
J'ai un commandement comme celui-ci.
wmctrl -lp | awk '/gedit/ { print $1 }'
Et je veux sa sortie dans un script python, j'ai essayé ce code
>>> 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'
>>>
Il semble que mon code soit incorrect, seul wmctrl -lp
a été exécuté et | awk '{print $ 1}'
est omis
Ma sortie attendue voudrait que 0x03800081
$ wmctrl -lp | awk '/gedit/ {print $1}'
0x03800081
Est-ce que quelqu'un aide s'il vous plaît.
La solution
Avec shell = True
, vous devez utiliser une seule ligne de commande au lieu d'un tableau, sinon vos arguments supplémentaires sont interprétés comme des arguments shell. Dans la documentation sur le sous-processus
:
Sous Unix, avec shell = True: Si args est une chaîne, il spécifie la chaîne de commande à exécuter via le shell. Si args est une séquence, le premier élément spécifie la chaîne de commande et tous les éléments supplémentaires seront traités comme des arguments de shell supplémentaires.
Votre appel devrait donc être:
subprocess.Popen("wmctrl -lp | sed /gedit/ '{print $1}'", shell=True, ...
Je pense que vous pouvez également avoir une citation simple non équilibrée.
Autres conseils
Etant donné que vous transmettez une séquence au programme, il pense que le canal est un argument à wmcrtrl
, comme si vous l'aviez fait
wmctrl -lp "|"
et ainsi l'opération de conduite réelle est perdue.
En faire une chaîne unique devrait en effet vous donner le bon résultat:
>>> 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()
''
Après quelques recherches, j'ai le code suivant qui fonctionne très bien pour moi. Il imprime essentiellement stdout et stderr en temps réel. J'espère que cela aidera quelqu'un d'autre qui en a besoin.
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
Lorsque cela est nécessaire, je pense qu'il est facile de collecter la sortie ou l'erreur dans une chaîne et de la retourner.