Python中set-o pipefail的等价物?

Python中set-o pipefail的等价物?,python,bash,shell,pipe,Python,Bash,Shell,Pipe,我有一些Python脚本,每个脚本都大量使用排序、uniq-ing、计数、gzip和gunzipping以及awking。作为对代码的第一次运行,我使用了subprocess.call(是的,我知道安全风险,这就是为什么我说这是第一次通过)shell=True。我有一个小助手功能: def do(command): start = datetime.now() return_code = call(command, shell=True) print 'Completed

我有一些Python脚本,每个脚本都大量使用排序、uniq-ing、计数、gzip和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)
但我不知道如何在Python中实现这一点。我想我可以直接打电话给bash,但这似乎是错误的。是吗

作为对这个特定问题的回答,我希望听到在纯Python中实现这种代码的替代方案,而不必求助于
shell=True
。但是,当我尝试使用
Popen
stdout=PIPE
时,代码大小就变大了。在一行上以字符串形式编写管道是一件好事,但如果有人知道用Python实现这一点的一种优雅的多行“正确且安全”的方法,我很乐意听到


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

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

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

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

为什么不创建一个满足您需要的bash脚本,并从Python调用该脚本?然后,您可以更好地控制整个管道,或者更好的是,或者只制作一个纯Bash脚本,或者将所有shell外部调用转换为本机PythonAh,对
do
的调用只是更大的Python脚本的一部分。它们(围绕子流程调用)有太多的逻辑,无法使用bash,这对于管道来说很好,但在处理数组和条件逻辑时却很差。谢谢,很好。最后我使用了
调用(['/bin/bash','-o',pipefail','-c',command])
。不过,这个想法完全基于您的答案:我从来没有想过打电话给bash。现在很明显。这个答案完全违背了shell=False提供的安全性。在这种情况下,我宁愿使用shell=True。@DavidRissatoCruz是的,好吧,OP明确地询问了关于执行作为字符串提供的管道的问题。为此,您需要一个shell,可以是使用
shell=True得到的隐式shell,也可以是我的答案得到的显式shell。在这两种情况下,您都将执行一个字符串,因此您需要信任该字符串。但是我不明白
shell=True
在这里有什么帮助,除非你的意思是它使信任问题变得更加明显。我从来没有说过你的解决方案不起作用,请不要这样认为。然而,他问“在纯Python中实现这种代码而不诉诸shell=True的替代方案”。。。“正确且安全的方法”,我的观点是调用
bash-c
与使用
shell=True
一样不安全。从安全角度看,两种解决方案完全相同。
def do(command):
  start = datetime.now()
  return_code = call([ '/bin/bash', '-c', 'set -o pipefail; ' + command ])
  ...