2014-02-12 2 views
4

У меня есть несколько скриптов Python, каждый из которых сильно использует сортировку, uniq-ing, подсчет, gzipping и gunzipping и прошивку. Как первый прогон кода, который я использовал subprocess.call (да, я знаю об угрозах безопасности, поэтому я сказал, что это первый проход) shell=True. У меня есть немного вспомогательные функции:Эквивалент набора -o pipefail в Python?

def do(command): 
    start = datetime.now() 
    return_code = call(command, shell=True) 
    print 'Completed in', str(datetime.now() - start), 'ms, return code =', return_code 
    if return_code != 0: 
     print 'Failure: aborting with return code %d' % return_code 
     sys.exit(return_code) 

скрипты используют этот помощник, как в следующих фрагментах:

do('gunzip -c %s | %s | sort -u | %s > %s' % (input, parse, flatten, output)) 
do("gunzip -c %s | grep 'en$' | cut -f1,2,4 -d\|| %s > %s" % (input, parse, output)) 
do('cat %s | %s | gzip -c > %s' % (input, dedupe, output)) 
do("awk -F ' ' '{print $%d,$%d}' %s | sort -u | %s | gzip -c > %s" % params) 
do('gunzip -c %s | %s | gzip -c > %s' % (input, parse, output)) 
do('gunzip -c %s | %s > %s' % (input, parse, collection)) 
do('%s < %s >> %s' % (parse, supplement, collection)) 
do('cat %s %s | sort -k 2 | %s | gzip -c > %s' % (source,other_source,match,output) 

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

Один вопрос, который я замечаю, что, когда команда в начале трубопровода не удается, вся команда будет по-прежнему удается со статусом выхода 0. В Баш исправить это с

set -o pipefail 

, но я не вижу, как это может выполняться в Python. Полагаю, я мог бы поставить явный вызов bash, но это кажется неправильным. Это?

Вместо ответа на этот конкретный вопрос мне бы хотелось услышать альтернативы реализации такого кода в чистом Python, не прибегая к shell=True. Но когда я пытаюсь использовать Popen и stdout=PIPE, размер кода взрывается. Есть что-то приятное в написании конвейеров на одной строке в виде строки, но если кто-нибудь знает элегантный многострочный «правильный и безопасный» способ сделать это в Python, я бы с удовольствием его услышал!

В стороне: ни один из этих сценариев никогда не принимает пользовательский ввод; они запускают пакетные задания на машине с известной оболочкой, поэтому я фактически отважился на зло shell=True, чтобы посмотреть, как все будет выглядеть. И они выглядят довольно легко читаемыми, и код кажется так кратким! Как удалить shell=True и запустить эти длинные конвейеры в необработанном Python, сохраняя при этом преимущества прервать процесс, если ранний компонент выходит из строя?

+0

Почему бы не создать один сценарий bash, который делает то, что вы хотите, и вызвать этот скрипт из Python? Тогда вы лучше контролируете всю конвейерную вещь. – Floris

+0

Или еще лучше, либо просто создать чистый скрипт Bash, либо преобразовать все внешние вызовы оболочки в собственный Python – DopeGhoti

+1

Ах, вызовы 'do' являются лишь частью гораздо более крупных скриптов Python. В них слишком много логики (вокруг вызовов подпроцессов), чтобы использовать bash, что отлично подходит для конвейеров, но плохо при работе с массивами и условной логикой. –

ответ

6

Вы можете установить pipefail в вызовах системы:

def do(command): 
    start = datetime.now() 
    return_code = call([ '/bin/bash', '-c', 'set -o pipefail; ' + command ]) 
    ... 

Или, как @RayToal отметил в комментарии, используйте -o вариант оболочки, чтобы установить этот флаг: call([ '/bin/bash', '-o', 'pipefail', '-c', command ]).

+1

Спасибо, хорошо. Я закончил использование 'call (['/ bin/bash', '-o', 'pipefail', '-c', command])'. –

+0

Конечно, это самый простой способ сделать это :) – Alfe

+0

Идея была полностью основана на вашем ответе. :) Я никогда не думал о вызове bash. Теперь это очевидно. –

Смежные вопросы