LINUX.ORG.RU

Python, os.system, subprocess.popen: как получить stdout запускаемой программы в виде строки?


0

1

Начал вчера изучать Питон.

Непойму, как сделать следующую вещь. Нужно запустить внешнюю программу и получить:

- статус завершения;
- строки, которые программа печатает в терминал.

Нашел две возможности запуска сторонних программ - функцию os.system (не рекомендуют ее к использованию) и функцию subprocess.popen (типа сейчас она самая модная).

Документации на русском практически никакой нет. На англицком понял только как ловить код завершения. А как получить вывод запущенной команды? Вывод нужен или в виде строки, или в виде списка строк.

Хочу увидеть кусок кода, который делает это.

★★★★★

from subprocess import Popen, PIPE

out, err = Popen('echo You are an lazy stupid motherfucker', shell=True, stdout=PIPE).communicate()
print out
anonymous ()
Ответ на: комментарий от anonymous

Так, а теперь объясните, почему поведение вашего кода отличается от вызова через os.system().

У меня есть шелл-скрипт, который самоубивается через определенное время (нужно на случай, если он зависнет). Я его пытаюсь запустить из Python.

Если я его запускаю так:

os.system('sh -c "./checkReadWriteProxy.sh"')

... то после самоубиения происходит нормальный возврат в Python. В консоли при этом видно сообщение:

sh: line 1: 12697 Убито

Так что через system все работает как надо. Но у функции system() я не могу получить потока вывода запущенной программы. Поэтому приходится пользоваться другими методами, например Popen.


Если же я запускаю скрипт по вашему совету через Popen:

from subprocess import Popen, PIPE

out, err = Popen('sh -c "./checkReadWriteProxy.sh"', shell=True, stdout=PIPE).communicate()
print out

... то после самоубиения скрипта возврата в Pythoon не происходит. В логе вижу ту же строку:

sh: line 1: 12737 Убито

но в Python управление не передается. На этом моменте все останавливается.

Вот. А нужно запустить скрипт так, чтобы после самоубиения управление вернулось бы в Python. Это же естественно. Но не пойму, как это сделать через Popen?

Xintrea ★★★★★ ()
Ответ на: комментарий от Xintrea

Но не пойму, как это сделать через Popen?

Видимо, нужно обзавестись новым набором генов.

#!/bin/sh
#/tmp/somebody-kill-me.sh

(while true; do echo Kill me; sleep 1; done) &
pid=$!

sleep 5
echo going to kill $pid
kill $pid
from subprocess import Popen, PIPE

out, err = Popen('/tmp/somebody-kill-me.sh', stdout=PIPE).communicate()
print 'Control returned? Yeah!'
print 'Out:\n', out
anonymous ()
Ответ на: комментарий от anonymous

Теперь посмотрим на то, получает ли ваш скрипт ошибку.

Bash:

$ cat /tmp/somebody-kill-me.sh
#!/bin/sh
exit 1

Python:

out, err = Popen('/tmp/somebody-kill-me.sh', stdout=PIPE).communicate()
print 'Control returned? Yeah!'
print 'Out:\n', out
print 'Err:\n', err

Результат:

Control returned? Yeah!
Out:
Err:
None

Где код ошибки, спрашивается?

Xintrea ★★★★★ ()
Ответ на: комментарий от Xintrea

Учимся пользоваться встроенной в питон документацией:

>>> print subprocess.Popen.communicate.__doc__
Interact with process: Send data to stdin.  Read data from
        stdout and stderr, until end-of-file is reached.  Wait for
        process to terminate.  The optional input argument should be a
        string to be sent to the child process, or None, if no data
        should be sent to the child.

        communicate() returns a tuple (stdout, stderr).

Т.е. в приведенном выше примере err это не код завершения, а содержимое stderr.

Правильный код вот:


p = Popen('./tst.sh', stdout=PIPE)
out, err = p.communicate()
print out
print p.returncode

anonymous ()
Ответ на: комментарий от anonymous

И еще, как у конструкции

(while true; do echo Kill me; sleep 1; done) &

ловить код возврата? Ведь по большому счету, именно он интересует.

Xintrea ★★★★★ ()
Ответ на: комментарий от Xintrea

Можно сделать wait $?, ($? это pid предыдущего порожденного процесса). Он, скорее всего, вернет тот же код (лень проверять).

anonymous ()
Ответ на: комментарий от anonymous

Видимо, нужно обзавестись новым набором генов.

Луговский тоже был материалистом. :)

pacify ★★★★★ ()
Ответ на: комментарий от Xintrea

Ведь по большому счету, именно он интересует.

Блжад. Так её и запускай тогда. Ты чо, тупой ваще?

anonymous ()
Ответ на: комментарий от zz

envoy

Нормальный api у subprocess, в отличие от urllib2. Желания взять и уе^Wпереписать/обернуть ни разу не возникало.

Единственно, иногда нужно хитро читать вывод и хук в communicate бы не помешал.

anonymous ()
Ответ на: комментарий от anonymous

Так её и запускай тогда.

Ее нельзя запускать, так как эта команда может повиснуть, об этом речь в топике. Если может повиснуть (а виснет оно на IO по сетевой FS) -> процесс превращается в зомби. А единственный способ убить зомби - убить родительский процесс.

Поэтому и нужно запускать команду как подпроцесс. Но у него нужно уметь получать вывод и код возврата (если он нормально отработает).

Xintrea ★★★★★ ()
Ответ на: комментарий от Xintrea

Поэтому и нужно запускать команду как подпроцесс. Но у него нужно уметь получать вывод и код возврата (если он нормально отработает).

как и сказали выше, указываешь subprocess.PIPE в stdout и stderr, - их можно считывать и с помощью простого Popen.stdout.read()

код возврата можно получить через Popen.wait() или через Popen.returncode.

также обрати внимание на параметр timeout метода Popen.wait()

чтобы отслеживать состояние процесса, можно обвернуть wait в цикл.

убить процесс можно по Popen.kill() или terminate() или послав сигнал соотв командой.

AGUtilities ★★★ ()
Ответ на: комментарий от Xintrea

Если может повиснуть (а виснет оно на IO по сетевой FS) -> процесс превращается в зомби.

Насколько знаю, зомби это уже завершившиеся процессы. Повисшими они быть не могут.

anonymous ()
Ответ на: комментарий от anonymous

Зомби - это процессы, которые попали в невозможность выхода по IO, потому что этого IO нет. Вот они и гоняют цикл ожидания по кругу.

Непонятно, почему до сих пор в ядре не сделали возможность нафиг прибивать такие процессы.

Xintrea ★★★★★ ()
Ответ на: комментарий от Xintrea

которые попали в невозможность выхода по IO, потому что этого IO нет

Чувак, выхлоп ps или top в студию. Бьюсь об заклад, в стейте будет «D», а не «Z».

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.