Python使用heredocs的子进程

5

我正在尝试一些Python的subprocess模块的示例,但是似乎无法使用heredoc语句。

这是我正在使用的简单示例:

import subprocess
a = "A String of Text"
p = subprocess.Popen(["cat", "<<DATA\n" + a + "\nDATA"])

当我运行上面的代码时,出现以下错误:
cat: <<DATA\nA String of Text\nDATA: No such file or directory

我做错了吗?这是否可能?如果可能,我该怎么做?


更新

只是想说,在真正的Python程序中,这不应该被执行,因为有更好的方法。

4个回答

6
"heredoc" 支持是一项 shell 的功能。subprocess.Popen 默认情况下不会通过 shell 运行您的命令,因此这个语法肯定不起作用。
然而,由于您已经在使用管道,所以没有必要使用 shell 的 heredoc 支持。只需将字符串a写入您刚刚启动的进程的标准输入管道即可。这正是 shell 使用 heredoc 做的事情。
您可以使用Popen.communicate()来完成此操作:
p.communicate(a)
communicate()函数的返回值包含了进程的输出(以两个流的形式呈现,请参见文档)。

我不太明白Shell参数的作用。文档在将其设置为true时解释得非常清楚,但是在将其设置为false时解释得不够充分。 - MitMaro
1
shell 参数为 False 时,subprocess 模块直接执行您指定的程序(在您的情况下,可能是 /bin/cat),并使用调用 Popen() 中指定的参数。不会对 shell 字符(如重定向或管道)进行任何解释,程序看到的正是您发送给它的内容。这就是为什么 cat 说找不到以 <<DATA 开头的文件名的原因。 - Greg Hewgill
感谢您的解释,澄清了一些事情。 - MitMaro

4

您正在将shell语法作为参数传递给cat程序。您可以尝试像这样执行:

p = subprocess.Popen(["sh", "-c", "cat <<DATA\n" + a + "\nDATA"])

但这个概念本身是错误的。你应该使用Python特性而不是在你的Python脚本中调用shell脚本。
在这种情况下,你需要知道shell的heredoc语法插入变量,因此你需要转义a中的所有文本,并确保其中没有DATA行。
对于Python等效代码,我认为最接近这个想法(假设你不只是想要print(a) ;-)) 是将变量的值传递给一个生成的进程的stdin:
p = subprocess.Popen(["program", ...], stdin=subprocess.PIPE)
p.communicate(a)

这是一个有帮助的答案。如果您能提供如何用Pythonic的方式实现此功能的代码建议,那将更好。 - Brian M. Hunt
就像我在问题中所说的一样,这只是使用 subprocess 玩耍而已,我永远不会像在 Python 中调用 cat 这样做。这更多是出于好奇而非在实际代码中使用。 - MitMaro
1
你应该使用Python的特性而不是在Python脚本中调用shell脚本。并非所有的shell特性都可以在Python中使用。一个heredoc可以被提供给许多不具有Python接口的程序,并且heredoc内容可以由Python动态生成。 - user5359531
1
@user5359531,我已经为此添加了一个微不足道的示例。 - Michał Górny

4

正如其他人指出的那样,您需要在shell中运行它。Popen通过使用shell=True参数使此操作变得容易。我得到以下输出:

>>> import subprocess
>>> a = "A String of Text"
>>> p = subprocess.Popen("cat <<DATA\n" + a + "\nDATA", shell=True)
>>> A String of Text

>>> p.wait()
0

2

从Python 3.5开始,您可以使用subprocess.run,例如:

subprocess.run(['cat'], input=b"A String of Text")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接