LINUX.ORG.RU

Запретить дочернему процессу выводить в stdout родителя

 ,


0

1

Доброй ночи ЛОРовцам. Пишу небольшой скрипт и в нём мне нужна функция, которая будет проверять установлен ли пакет в системе (в Debian-подобной). Работает так: открываю новый процесс, там 'dpkg -s <пакет>', ищу ввв выводе Status, сравниваю и возвращаю True/False.

В чем проблема: дочерний процесс (dpkg) выводит в stdout скрипта результат выполнения, ведь он наследует открытые дескрипторы.

Мне нужно читать этот вывод (от dpkg), но что бы он не засорял вывод скрипта.

Код:

#!/usr/bin/env python3

import sys
from subprocess import Popen, PIPE

def is_installed(pkg):
    proc = Popen(['dpkg', '-s', pkg], stdout=PIPE)

    proc.wait()
    
    output = str(proc.stdout.read())

    if not output.find("Status: install ok installed") == -1:
        return True
    else:
        return False

print(is_installed(sys.argv[1]))

Есть одна особенность: он (дочерний процесс) пихает свой вывод в мой вывод только в случае неудачи dpkg (то есть если пакет не установлен). Если функция возвращает False, то перед False у меня весь вывод дочернего процесса (dpkg), иначе у меня просто вывод из одной строки — 'True'. Я не пойму с чем это связанно.

Вывод:

nrdgrauf@localhost: ~ > ./test.py wine
True
nrdgrauf@localhost: ~ > ./test.py vim
dpkg-query: package 'vim' is not installed and no information is available
Use dpkg --info (= dpkg-deb --info) to examine archive files,
and dpkg --contents (= dpkg-deb --contents) to list their contents.
False
nrdgrauf@localhost: ~ > dpkg -s vim
dpkg-query: package 'vim' is not installed and no information is available
Use dpkg --info (= dpkg-deb --info) to examine archive files,
and dpkg --contents (= dpkg-deb --contents) to list their contents.
nrdgrauf@localhost: ~ > 

P.S. — Приму любые улучшения кода, т.к. в Python почти полный 0x0.

    if not output.find("Status: install ok installed") == -1:
        return True
    else:
        return False

На питоне будет:

    return not output.find("Status: install ok installed") == -1

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

тогда уже

"Status: install ok installed" in output:
чтоб совсем по-питонски было

anonymous ()

При ошибке он пишет в stderr, выводи stderr в /dev/null. В Python 3.3 это просто:

from subprocess import Popen, PIPE, DEVNULL
proc = Popen(['dpkg', '-s', pkg], stdout=PIPE, stderr=DEVNULL)

Если нужна совместимость с 3.2, где не было константы subprocess.DEVNULL, можно вручную открыть файл, путь к которому лежит в os.devnull.

Что касается советов по улучшению качества кода, то советуют не применять Popen.wait(), так как могут переполниться буферы ввода/вывода и произойдёт deadlock, так как твой скрипт будет ждать процесс, а он — освобождения буферов. Используй Popen.communicate(). Или вообще используй subprocess.run() вместо вызова Popen (в Python 3.5).

Ну и ещё хочу сказать вот что: неплохо было бы добавить в скрипт опцию --verbose, которая будет выводить весь выхлоп dpkg в консоль. Когда что-нибудь сломается, те, кому придётся с этим разбираться, скажут тебе спасибо.

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

все верно

Все верно сказано, тем более в доке написана

stdin, stdout and stderr specify the executed program’s standard input, standard output and standard error file handles, respectively. Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None. PIPE indicates that a new pipe to the child should be created. With the default settings of None, no redirection will occur; the child’s file handles will be inherited from the parent. Additionally, stderr can be STDOUT, which indicates that the stderr data from the child process should be captured into the same file handle as for stdout.

krasoffski ()
Ответ на: все верно от krasoffski

А, да, кстати, вместо DEVNULL можно выводить stderr в STDOUT. Там всё равно строки «Status: installed ok installed», наверное, не будет.

proud_anon ★★★★★ ()

Да, я забыл про stderr.. Всем спасибо за советы.

nrdgrauf ()

А просто закрыть дескриптор нельзя? Ну в крайнем случае перенаправить в null.

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