【问题标题】:How to provide input to a python subprocess call that expects filename, rather than variable?如何为需要文件名而不是变量的python子进程调用提供输入?
【发布时间】:2012-02-20 04:55:46
【问题描述】:

我正在尝试在 python 中调用 shell 脚本 (segment.sh)。 在控制台产生正确结果的语法是:

> ./segment.sh ctb file.txt utf-8 0

可以看出,这个 shell 脚本需要一个文本文件作为输入。我想要做的是打开文件并在python中读取它的内容(以后最好从HTML POST表单),并以某种方式将包含内容的变量传递给python子进程 打电话。

以下功能不起作用。但如果我只提供一个文件名,例如:

Popen(["/bin/bash", "./segment.sh", "ctb", "file.txt", "utf-8", "0"])`

然后,它会工作,但我想从变量传递输入。能指点一下吗?

def pySegment(text):
    op = subprocess.Popen(["/bin/bash", "./segment.sh", "ctb", "utf-8", "0"],
                      stdout = subprocess.PIPE,
                      stdin  = subprocess.PIPE,
                      stderr = subprocess.STDOUT,                          
                      )
    results = op.communicate(input=text)[0]
    return results

if __name__ == "__main__":
    filename = "./file.txt"
    text = open(filename).read()
    result = pySegment(text)
    print result

【问题讨论】:

  • 如果segment.sh 实际上是打开您提供的文件名并读取其内容,并且不支持从标准输入读取,那么不幸的是,您将不得不要么将文本保存到文件,要么修改segment.sh。这就是 Unix 编程的理念之一:让每个程序都能够像过滤器一样运行——换句话说,能够从标准输入读取并写入标准输出。
  • 感谢您的评论,@voithos。 segment.sh 脚本本身正在调用 java 程序: JAVACMD="java -mx2g -cp $BASEDIR/seg.jar edu.stanford.nlp.ie.crf.CRFClassifier -sighanCorporaDict $DATADIR -testFile $file -inputEncoding $enc -sighanPostProcessing true $ARGS"。所以,我不确定这个程序是否可以接受除了文件之外的标准输入。请指教。
  • 同样的观点成立。您的脚本正在运行的 Java 程序需要能够从标准输入中读取。看起来你在这里反对你的工具。不幸的是,并非所有程序都是在考虑标准输入/输出的情况下编写的。但是只使用文件有问题吗?只需在文件而不是变量上运行pySegment,然后打开分段文件并读取它。
  • 尝试将- 作为文件名传递。如果- 作为文件名传递,则从标准输入读取是一种常见的约定。
  • 如果 ./segment.sh#!/bin/bash shebang 并且它是可执行的 (chmod +x segment.sh),你可以删除 /bin/bash

标签: python shell variables subprocess pipe


【解决方案1】:

我建议使用named pipe:

import os, tempfile, shutil, subprocess

temp_dir = tempfile.mkdtemp()
filename = os.path.join(temp_dir, 'file.txt')
text = '<text>'
os.mkfifo(filename)

try:
    subprocess.Popen(('segment.sh', 'ctf', filename, 'utf-8', '0'))
    with open(filename, 'w') as f:
        f.write(text)
finally:
    shutil.rmtree(temp_dir)

命名管道将提供与文件相同的接口,而无需真正根据需要创建该文件。

【讨论】:

  • os.mkfifo(filename) 应该在try/finally 之外。为了避免冲突,可以使用tempfile.mkdtemp()(之后使用shutil.rmtree())。
  • @J.F.Sebastian 不错的建议。非常感谢您的评论。
  • @jcollado 非常感谢命名管道的代码和建议。这对我来说是新事物。所以 text = '' 是我可以指定要分割的内容的地方,对吧?
  • @user1172819 是的,没错。另请注意,您可以将管道用作任何其他文件,因此您可以根据需要将任意数量的write 调用与基于所需的任何其他输入的一些处理混合在一起,也就是说,您不需要将所有内容都放在变量中写之前。
  • @jcollado,是的!该代码有效。非常感谢你帮助我。我还要感谢 voithos 和 J.F. Sebastian 非常有用的 cmets。非常感谢!
猜你喜欢
  • 2014-11-10
  • 1970-01-01
  • 1970-01-01
  • 2013-10-07
  • 2018-03-30
  • 2011-10-17
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
相关资源
最近更新 更多