Python中set -o pipefail的等价物?

时间:2014-02-12 23:58:34

标签: python bash shell pipe

我有一些Python脚本,每个脚本都大量使用排序,统一,计数,gzipping和gunzipping,以及awking。作为第一次运行代码,我使用subprocess.call(是的,我知道安全风险,这就是为什么我说它是第一次通过)shell=True。我有一个小帮手功能:

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成功。在bash中我用

修复此问题
set -o pipefail

但我不知道如何在Python中完成此操作。我想我可以明确地调用bash,但这似乎是错误的。是吗?

代替对该特定问题的回答,我很想听到在纯Python中实现这种代码的替代方法而不诉诸shell=True。但是,当我尝试使用Popenstdout=PIPE时,代码大小就会爆炸。将管道作为字符串写在一行上有一些好处,但如果有人知道优雅的多行“正确和安全”的方式在Python中这样做我很想听到它!

抛开:这些脚本都没有用户输入;他们在具有已知shell的机器上运行批处理作业,这就是为什么我真正冒险进入邪恶的shell=True只是为了看看事情的样子。它们看起来很容易阅读,代码似乎所以简洁!如何在原始Python中删除shell=True并运行这些长管道,同时如果早期组件失败,仍然可以获得中止流程的优势?

1 个答案:

答案 0 :(得分:7)

您可以在系统调用中设置pipefail

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

或者,正如@RayToal在评论中指出的那样,使用shell的-o选项来设置此标志:call([ '/bin/bash', '-o', 'pipefail', '-c', command ])