【问题标题】:How should subprocess be used for commands featuring here-documents and multiple pipes?subprocess 应该如何用于具有 here-documents 和多个管道的命令?
【发布时间】:2015-03-08 16:06:09
【问题描述】:

如何使用subprocess 而不是os 在 Bash 中运行如下命令?:

import os
text     = "~|||-:this is text:-|||~"
fileName = "sound.wav"
command  =\
    "IFS= read -d \'\' text <<EOF\n" +\
    text + "\n" +\
    "EOF\n" +\
    "echo \"${text}\" | sed -e 's/\([[:punct:]]\)//g' | text2wave -scale 1 -o " + fileName
os.system(command)

理想情况下,这将在 Bash 中计算为以下内容:

IFS= read -d '' text <<EOF
~|||-:this is text:-|||~
EOF
echo "${text}" | sed -e 's/\([[:punct:]]\)//g' | text2wave -scale 1 -o sound.wav

请注意,我在命令中同时使用了 here-document 和多个管道。我知道我不需要在命令中进行 sed 处理,但我正在尝试使用 subprocess 在 Bash 中运行这样的命令。

目前,我在subprocess 命令过程中看到了一些“管道”的实现,但与简单的竖线符号相比,这些实现非常冗长。我可以想象这个场景会变成多管的噩梦。至于这里的文档,我不知道如何用subprocess 来实现它们。我会重视您可能拥有的有关实施此功能的任何指导。

程序 text2wave 是Festival 的一部分,如果您有兴趣的话。

【问题讨论】:

  • 所有的文本转换都可以轻松移植到 Python 中,然后只剩下 text2wave 命令在外部执行。实际上,如果 Festival 库有一个 Python 包装器,您也可以用它替换它,我不会感到惊讶。
  • 实际上整个while 循环是不优雅和多余的;您应该直接将此处的文档提供给sed
  • 非常感谢您的 cmets。正如我所提到的,我知道有一些优雅的方法可以在 Python 中实现命令的功能,但这并不是我想要做的。我正在尝试采用 given 命令,该命令具有此处文档和多个管道,并将其与 Python 的 subprocess 模块一起使用。我正在尝试学习如何在使用subprocess 时处理此处的文档和管道。

标签: python pipe subprocess heredoc


【解决方案1】:

如果 shell 非常适合您的问题,那么一定要使用 shell。

您有时想要将某些东西移植到 Python 的原因至少对我来说是出于更好的控制:您希望能够在管道中间(在您的 shell 脚本中是什么)捕获错误,或者您想要在处理过程中的某个地方保留结果,这在 shell 中很麻烦,因为管道是一维的。

如果您真的坚持,那么您提到(但未链接到)的资源就是通常的做法;但由于上述原因,这并不是特别“常见”。

这是一个不使用 shell 的例子,改编自this question

from subprocess import Popen, PIPE

input="""
~|||-:this is text:-|||~
"""
p1 = Popen(['sed', r's/\([[:punct:]]\)//g'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
p2 = Popen(['text2wave', '-scale', '1', '-o', fileName],
    stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
p1.stdin.write(input)
p1.stdin.close()
output, error = p2.communicate()
p1.wait()
print('status: {0}\nOutput: {1}\nError:{2}'.format(p2.returncode, output, error))

我确信这可以扩展到两个以上的子进程,但坦率地说,如果你真的需要解决这个问题,我也很确定你做错了。

... 而且,正如 cmets 中已经指出的那样,sed 部分可以很容易地被 simple Python snippet 替换;

from string import punctuation
input = input.translate(None, punctuation)

还要注意 Python 多行字符串如何替换此处的文档。

【讨论】:

  • 赞成总体思路。我只会留下text2wave 子进程(如果还没有易于安装的 Python 绑定)。您的代码可能会在 input 中引入不必要的前导/尾随空格(在这种情况下无关紧要)。如果sed 认为(\( 在正则表达式中不同,则使用原始字符串文字。我不确定语言环境对string.punctuation[[:punct:]] 的影响——某些环境中某些数据的细微错误的来源。这就是为什么,除非必须,否则一般不应该移植软件——成本比最初可能出现的要高。
  • ...虽然事实上,括号在那个特定的 sed 脚本中完全是多余的,现在我实际查看了它。
  • 使用stderr=PIPEerror 中获取除None 以外的任何内容。
【解决方案2】:

Here-docs 和管道 (|) 是 shell 的属性。如果您想按字面意思运行命令,请使用shell=True

from subprocess import check_call

check_call(r"""IFS= read -d '' text <<EOF
~|||-:this is text:-|||~
EOF
echo "${text}" | sed -e 's/\([[:punct:]]\)//g' | text2wave -scale 1 -o sound.wav
""", shell=True)

作为@tripleee said,部分甚至整个命令都可以用Python来实现。

【讨论】:

    【解决方案3】:

    os.popen 的演示: :

    print os.popen(r"""
        /bin/bash<<EOF
    shell line 1...
    shell line 2...
    shell line 3...
    EOF
    """).read()
    

    subprocess.Popen("""
        /bin/bash<<EOF
    shell line 1...
    shell line 2...
    shell line 3...
    EOF
    """, shell=True)
    

    【讨论】:

      猜你喜欢
      • 2012-10-31
      • 2017-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      相关资源
      最近更新 更多