У меня есть несколько скриптов 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, сохраняя при этом преимущества прервать процесс, если ранний компонент выходит из строя?
Почему бы не создать один сценарий bash, который делает то, что вы хотите, и вызвать этот скрипт из Python? Тогда вы лучше контролируете всю конвейерную вещь. – Floris
Или еще лучше, либо просто создать чистый скрипт Bash, либо преобразовать все внешние вызовы оболочки в собственный Python – DopeGhoti
Ах, вызовы 'do' являются лишь частью гораздо более крупных скриптов Python. В них слишком много логики (вокруг вызовов подпроцессов), чтобы использовать bash, что отлично подходит для конвейеров, но плохо при работе с массивами и условной логикой. –