您的代码存在一些小问题,但总的来说,是的,这就是您运行子进程的方式。
- 尽可能选择
subprocess.run 而不是裸露的Popen。这也可以避免您认为令人困惑的行为;请参阅下一个要点。
- 因此,如果您先运行
Popen,然后运行communicate,则输出是一个具有两个值的元组,即标准输出和已完成进程的标准错误。
- 但是您将标准输出重定向到一个文件,所以
stdout=subprocess.PIPE 当然是不必要的并且什么也不会产生。 (因为你没有用stderr=subprocess.PIPE捕获stderr,所以它最终会包含None;如果有任何错误输出,它只是简单地显示给用户,而不是Python。)
- 您的 shell 脚本过于复杂。将其减少为单个进程将避免需要
shell=True,即generally something you should strive for.
- 但更重要的是,该脚本可以在原生 Python 中重新实现,这将使其更加通用,并且对于不熟悉 shell 脚本和 Python 的任何人来说都更容易理解。 (当然,shell 的表述会更加简洁,至少在重构之后是这样。)
明显的 Python 实现如下所示
from pathlib import Path
...
with open("sample.txt", "r") as lines, \
Path("~/out.txt").expanduser().open("w") as output:
for line in lines:
if "patternA" in line:
output.write(line.replace('foo', 'bar'))
显然我们不得不猜测您的 sed 脚本实际上做了什么,因为您已将其替换为占位符。
与subprocess.run 相同,并避免使用 shell 编程反模式,
from pathlib import Path
...
with Path("~/out.txt").expanduser.open("w") as output:
subprocess.run(
['sed', '/patternA/something', 'sample.txt'],
stdout=output, text=True, check=True)
您想避免使用 [无用的cat](useless use of cat 和 useless grep; 并且不碍事,您不需要管道,因此不需要外壳。
如果您想从子流程中检索状态信息,请将subprocess.run 的结果分配给您可以检查的变量,例如r;错误状态将在r.resultcode 中(尽管check=True 保证为0)。
Python 不会让您将capture_output=True 与stdout=... 和/或stderr=... 混合使用,因此如果您想查看是否有错误输出(即使某些工具成功,也可能会出现警告消息)您不得不拆分操作。可能是这样的:
import logging
from pathlib import Path
...
r = subprocess.run(
['sed', '/patternA/something', 'sample.txt'],
capture_output=True, text=True, check=True)
with Path("~/out.txt").expanduser().open("w") as output:
output.write(r.stdout)
if r.stderr:
logging.warn(r.stderr)
最后,os.path.expanduser() 或 pathlib.Path.expanduser() 是解析 ~/out.txt 到主目录中的文件所必需的。您通常永远不需要os.chdir() 来查找文件;如果它不在当前目录中,只需指定其路径名。另见What exactly is current working directory?