无法使用子进程从标准输出获取结果

Posted

技术标签:

【中文标题】无法使用子进程从标准输出获取结果【英文标题】:Unable to get result from stdout with subprocess 【发布时间】:2017-11-06 06:10:54 【问题描述】:

我需要在发出命令后让结果显示在控制台中。

例如: H:/path/to/openssl.exe x509 -in H:/path/to/cert.pem -noout -subject

当放置在打开的 cmd.exe 中时,此命令会生成加密证书的值,但我无法获得与使用 subprocess 模块返回的变量相同的结果。

到目前为止,我已经尝试过(cmd 是上面的例子):

# checking if output is going to stderr
out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
# attempt with Popen
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).communicate()[0]
# attempt with Popen not using communicate()
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
out = p.stdout.read()

所有print (out) 值都是b''

我什至将cmd 拆分为多个args 用于check_output()Popen(),这样它看起来像:["H:/path/to/openssl.exe", "x509 -in H:/path/to/cert.pem -noout -subject"]

我确实有另一种方法来实现该结果,但这需要运行一个包含以下命令的 .bat 文件:start cmd /k "... > H:/path/to/temp.txt" 其中... 是此处有问题的命令。这会将 .exe 输出重定向到一个临时文件(出于安全目的),我在将内容读入变量后将其删除。

支持信息:

如果运行命令的第一部分(.exe),它将打开该程序的自定义控制台。该命令的其余部分在该控制台中执行,然后该控制台关闭。但是,如果在 cmd.exe 控制台中使用整个命令系列,则输出将显示在该控制台中。因此,为什么在我的 .bat 替代方法中使用" ",因为它将它作为一个整体命令发布到打开的控制台。

使用subprocess.Popen(["cmd.exe"]) 会在C:\Python36> 处打开一个控制台,我可以发出相关命令并获得我想要的确切结果。不过,我似乎无法以蟒蛇的方式抓住它。但是,subprocess.Popen(["cmd.exe", "H:/path/to/openssl.exe"]) 不执行第二条命令;打开控制台但不启动 .exe 控制台。

更新:

我能够得到一个结果,但我不喜欢我是如何到达那里的。是否有更短的方法来执行以下操作:

p = subprocess.Popen("H:/path/to/openssl.exe", stdin=PIPE, stdout=PIPE)
# b for byte-type requirement
p.stdin.write(b"x509 -in H:/path/to/cert.pem -noout -subject")
p.stdin.close()
out = p.stdout.read()

我也可以通过以下方式生成结果:

p = os.popen("start cmd /k H:/path/to/openssl.exe -in H:/path/to/cert.pem -noout -subject")

尝试在上面添加.read(),但打印一个空行

【问题讨论】:

一般提示:在 Popen 参数中设置 shell=True 是不安全的。尝试将shlex.split(cmd) 作为您的第一个参数传递给Popen @Billy 仍然返回b''。我试图弄清楚为什么即使使用拆分命令,Popen() 也没有执行上面“支持信息”中所述的第二个命令。 @Billy 更新问题 【参考方案1】:

我不习惯在 windows 上使用 python,但是试试下面的。不要使用shell=True,而是尝试使用这个 cmd:

cmd = ["H:/path/to/openssl.exe", "x509", "-in", "H:/path/to/cert.pem", "-noout", "-subject"]

【讨论】:

应该是评论而不是答案。 这不起作用,因为Popen() 没有执行列表中的下一个命令【参考方案2】:

您是否尝试过将整个命令存储为原始字符串并使用 shell=True 传递它

cmd = r'H:/path/to/openssl.exe x509 -in H:/path/to/cert.pem -noout -subject' p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out = p.communicate() print(out[0]) #for std out print(out[1]) # for std err

编辑:这对我来说适用于 stderr,

print p.communicate()[1]

b'unable to load certificate\r\n3636:error:0D07209B:asn1 encoding routines:ASN1_ get_object:too long:./crypto/asn1/asn1_lib.c:142:\r\n3636:error:0D068066:asn1 en coding routines:ASN1_CHECK_TLEN:bad object header:./crypto/asn1/tasn_dec.c:1281: \r\n3636:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 erro r:./crypto/asn1/tasn_dec.c:380:Type=X509\r\n3636:error:0906700D:PEM routines:PEM _ASN1_read_bio:ASN1 lib:./crypto/pem/pem_oth.c:83:\r\n'

【讨论】:

假设您的意思是 print(out[0])print(out[1])。是的,我有并且都返回为b''。几乎就像命令没有按顺序执行一样。我有一个并行的问题帖子here 对我来说发生的事情是我正在有效地运行 openssl.exe,传递有效的参数并获得回报。但是,它似乎不会进入 cmd.exe 标准输出;它留在 openssl.exe 中。我需要做C:\Users\Me> H:/path/to/openssl.exe x509 -in H:/path/to/cert.pem -noout -subject 并抓住stdout;当我通过 Popen() 运行时,它会产生一个结果,但它被放置在 CLI 之前的一行上,而不是在 stdout 中。

以上是关于无法使用子进程从标准输出获取结果的主要内容,如果未能解决你的问题,请参考以下文章

从提升的子进程获取错误和标准输出

无法在 Django 视图中获取子进程输出

使用 libuv 捕获子进程的标准输出

重定向子进程标准输出

无法从 2 个 forks() 获取 3 个子进程

获取 node.js 中所有嵌套子进程的标准输出