Как запустить программу Perl из сценария Python, получая при этом логи и избегая сбоев для кода возврата сценария: -9 - PullRequest
1 голос
/ 11 апреля 2020

Я использую Python для запуска длинной (4-60 минут работы) Perl программы с разными входами. Программа Perl старая и с закомментированным предупреждением об использовании. Различные входы используют разные части кода.

# use warnings FATAL => 'all';

Все входы работают, когда я запускаю CMD с bash!

perl -I/storage my_script_entance.pl --ex_config R....

Некоторые входы cra sh, когда я их запускаю используя Popen:

 run_script('perl -I/storage my_script_entance.pl --ex_config R....')


 def run_script(cmd):

    args = shlex.split(f'{cmd}')

    # p = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE)

    p = sp.Popen(args, stderr=sp.PIPE)

    output, err = p.communicate()
    print(f'output: {output}')
    print(f'err: {err}')

    rc = p.returncode

    print(f"script return code: {rc}")

Аварии выглядят так:

output: None
err: b''
script return code: -9

они появляются в разных местах кода

Моя среда: Kubernetes с Alpine Docker контейнеры с:

  • perl5 (версия 5, версия 30 subversion 1)
  • Python 3.8.2

Почему сценарии запускаются из Чем отличается popen от запуска скриптов от bash? Есть ли способ запустить их одинаково.

Я прочитал документацию Popen и попытался использовать функцию запуска с check = False с этим примером . но не смог прочитать выходные сообщения об ошибках.

Я попробовал некоторые опции, используя pexpect, но пока не получилось.

Есть ли способ запустить сценарии, как в bash, и просто напечатать стандартный вывод и ошибка?

PS

Я использую Python для запуска Perl из-за превосходной библиотеки Кафки (Perl Библиотека Кафки является нестабильной и жесткой)

Ответы [ 2 ]

2 голосов
/ 12 апреля 2020

Хорошо, @ikegami дал правильно, мои проблемы были усугублены:

  • -9 код возврата был насильственным отключением perl демонов, возможно, с помощью docker VM или MacOs или .. .
  • Я мог только слушать стандартный вывод в моем скрипте.
  • Я звонил Perl из необычного контекста.
  • Любое изменение, например args = ['bash', '-c', cmd] создает буферы с помощью perl и, возможно, Bash.

, поэтому я спросил друга, и мы нашли два решения, а именно:

  1. Доступ к стандартному выводу после вызова wait() и игнорируя стандартную ошибку блокировки.
  2. Использование библиотеки селекторов Python для регистрации обоих конвейеров и их разделения по мере их поступления.

Оба работают, только если для Perl установлено значение грипп sh выводится сразу:

$| = 1;

вот пример perl скрипт:

#!/usr/bin/perl -w
use strict;
# use warnings FATAL => 'all';

use feature qw/say/;

use Try::Tiny;
use File::Basename;
use Data::Dumper;
use Getopt::Long;
use Data::Dumper;
use YAML::XS 'LoadFile';

use JSON::MaybeXS qw(encode_json decode_json);
use JSON qw();


$| = 1;

say("Abra dabra");

my $noise = 'Mooo';
# my $bot_init_args;

GetOptions(
    'noise=s' => \$noise,
    # 'bot_init_args=s' => \$bot_init_args
);

if (defined $noise) {

    my $limit = 5;

    print STDERR "something awful\n";

    for(my $i=0; $i < $limit; $i++){

        say("$i of $limit: $noise");
        sleep(2);
    }

    print STDERR "something embarrassing\n";

    sleep(2);

    exit 0;
}

А вот два решения в Python 3.82:

    #!/usr/bin/env python

import shlex
import subprocess as sp

import selectors


def read_stdo(something):

    print('standard output')
    print(f'{type(something)}')
    print(something.decode("utf-8"))


def read_stderr(something):
    print('standard error')
    print(f'{type(something)}')
    print(something.decode("utf-8"))


def channel(cmd):

    args = shlex.split(cmd)

    sel = selectors.DefaultSelector()

    p = sp.Popen(args=args, stderr=sp.PIPE, stdout=sp.PIPE, bufsize=0)

    sel.register(p.stdout, selectors.EVENT_READ, data=read_stdo)
    sel.register(p.stderr, selectors.EVENT_READ, data=read_stderr)

    while p.poll() is None:

        print(p.poll())
        events = sel.select()
        for key, mask in events:

            line = key.fileobj.readline()

            print(f'{type(line)}')
            key.data(line)

    return_val = p.wait()
    print(f'return val: {return_val}')
    # return lines


def run_script(cmd):

    print("Reading command Standard Output without listening to Standard Error")

    args = shlex.split(f'{cmd}')

    print(f'cmd: {cmd}')
    print(args)
    # p = sp.Popen(args=['/bin/bash', '-c', 'printenv;', ' sleep 25;', ' pwd'], stdout=sp.PIPE)
    p = sp.Popen(args=args, stderr=sp.PIPE, stdout=sp.PIPE, bufsize=0)

    line = p.stdout.readline()
    while line:
        print(line.decode("utf-8"))
        line = p.stdout.readline()
    # p = sp.run(['bash', '-c', cmd], stdout=sp.PIPE, stderr=sp.PIPE)

    # output, err = p.communicate()
    # print(f'output: {output.decode("utf-8")}\n')

    # if err is not None:
    #     print(f'err: {err.decode("utf-8")}')

    p.wait()
    rc = p.returncode

    print(f"script return code: {rc}")


if __name__ == "__main__":

    _cmd = 'perl -I/storage /storage/annoyingPerl.pl --noise quack'
    run_script(_cmd)

    channel(f'{_cmd} barf')
1 голос
/ 11 апреля 2020

Вот пример использования subprocess.run() и запуска сценария Perl через bash:

Сначала я создал тест Perl script p.pl:

use feature qw(say);
use strict;
use warnings;

for my $i (0..$#ARGV) {
    my $arg = $ARGV[$i];
    say "ARG $i: $arg";
}
sleep 2;
say STDERR "STDERR: no errors";
sleep 1;
exit 2;

Затем скрипт Python:

import subprocess as sp

def main():
    cmd = R"""perl p.pl -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
    """
    #print(cmd)

    completed_proc = sp.run(['bash','-c', cmd], stdout=sp.PIPE, stderr=sp.PIPE)

    print('output: {}'.format(completed_proc.stdout))
    print('err: {}'.format(completed_proc.stderr))
    print('script return code: {}'.format(completed_proc.returncode))

main()

Вывод :

output: b"ARG 0: -input\nARG 1: eggs.txt\nARG 2: -output\nARG 3: spam spam.txt\nARG 4: -cmd\nARG 5: echo ''\n"
err: b'STDERR: no errors\n'
script return code: 2
...