Python中set -o pipefail的等价物?

Ray*_*oal 8 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)
Run Code Online (Sandbox Code Playgroud)

脚本使用此助手,如下面的代码段所示:

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)
Run Code Online (Sandbox Code Playgroud)

还有更多像这样的,有些甚至更长的管道.

我注意到的一个问题是,当管道中的某个命令失败时,整个命令仍然会以退出状态0成功.在bash中,我修复此问题

set -o pipefail
Run Code Online (Sandbox Code Playgroud)

但我不知道在Python中如何做到这一点.我想我可以明确地调用bash,但这似乎是错误的.是吗?

代替对该特定问题的回答,我很乐意听到在纯Python中实现这种代码的替代方法,而无需借助shell=True.但是当我尝试使用Popen并且stdout=PIPE代码大小爆炸时.将管道作为一个字符串写在一行上有一些不错的东西,但如果有人知道优雅的多行"正确和安全"的方式在Python中这样做我很想听到它!

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

Alf*_*lfe 6

您可以设置pipefail对系统的调用:

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

或者,正如@RayToal在注释中指出的那样,使用-oshell 的选项来设置这个标志:call([ '/bin/bash', '-o', 'pipefail', '-c', command ]).

  • 我并不是说你的解决方案不起作用,请不要这样认为。然而,他询问“在纯 Python 中实现此类代码而不诉诸 shell=True 的替代方案”...“正确且安全的方式”,我的观点是调用 `bash -c` 与使用 `shell= 一样不安全确实`。从安全角度来看,两种解决方案完全相同。 (2认同)