当 PIPE 在命令中时,Python 中使用 subprocess.run 的正确方法

Correct way in Python to use subprocess.run when PIPE is in the command

我正在尝试编写一个 python 脚本来执行以下终端命令:

echo -n | openssl s_client -connect {host}:{port} | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > {host}_{port}.cert

如果我尝试将命令分解为参数以传递给 subprocess.run 它不起作用(有些东西是 运行 但它没有像我希望的那样存储证书。

使用以下语法可以正确执行命令,但我担心这不是最佳实践,我想了解正确的执行方式:

store_certificate_command = f"echo -n | openssl s_client -connect {host}:{port} | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > {host}_{port}.cert"

subprocess.run(store_certificate_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

执行此操作的正确方法是将自己限制在非Python 可执行文件的最小数量。在这种情况下,echo 不是必需的,Python 可以完成 sed 正在做的工作,也可以写入结果文件。一个干净的解决方案是这样的:

import subprocess

with open(f'{host}_{port}.cert', 'wb') as outf,\
     subprocess.Popen(['openssl', 's_client', '-connect', f'{host}:{port}'], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc:
    for line in proc.stdout:
        if b'-BEGIN CERTIFICATE-' in line:
            outf.write(line)
            break
    else:
        raise ValueError("BEGIN CERTIFICATE not found in output")

    for line in proc.stdout:
        outf.write(line)
        if b'-END CERTIFICATE-' in line:
            break
    else:
        raise ValueError("END CERTIFICATE not found in output")

我改为使用 subprocess.Popen,因为它允许您以流方式处理输出(与 shell 管道的工作方式相同),但考虑到相对较小的输出,subprocess.run 也可能工作得很好。 ValueError 不是绝对必要的,但我喜欢把它们放在那里,这样当输出不是你期望的时候你就会失败。