gpt4 book ai didi

Python 子进程 : Giving stdin, 读取标准输出,然后提供更多标准输入

转载 作者:行者123 更新时间:2023-12-01 12:05:44 26 4
gpt4 key购买 nike

我正在使用一款名为 Chimera 的科学软件。对于这个问题下游的一些代码,它要求我使用 Python 2.7。

我想调用一个进程,给这个进程一些输入,读取它的输出,基于它给它更多的输入,等等。

我用过 Popen打开进程,process.stdin.write传递标准输入,但后来我在进程仍在运行时试图获取输出卡住了。 process.communicate()停止进程,process.stdout.readline()似乎让我陷入无限循环。

这是我想做的一个简化示例:

假设我有一个名为 exampleInput.sh 的 bash 脚本.

#!/bin/bash
# exampleInput.sh

# Read a number from the input
read -p 'Enter a number: ' num

# Multiply the number by 5
ans1=$( expr $num \* 5 )

# Give the user the multiplied number
echo $ans1

# Ask the user whether they want to keep going
read -p 'Based on the previous output, would you like to continue? ' doContinue

if [ $doContinue == "yes" ]
then
echo "Okay, moving on..."
# [...] more code here [...]
else
exit 0
fi

通过命令行与之交互,我会运行脚本,输入“5”,然后,如果它返回“25”,我会输入“yes”,如果不是,我会输入“no”。

我想在我通过的地方运行一个 python 脚本 exampleInput.sh “5”,如果它给我“25”,那么我通过"is"

到目前为止,这是我能得到的最接近的:
#!/home/user/miniconda3/bin/python2
# talk_with_example_input.py
import subprocess
process = subprocess.Popen(["./exampleInput.sh"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE)
process.stdin.write("5")

answer = process.communicate()[0]

if answer == "25":
process.stdin.write("yes")
## I'd like to print the STDOUT here, but the process is already terminated

但这当然失败了,因为在“process.communicate()”之后,我的进程不再运行了。

(以防万一/仅供引用):实际问题

Chimera 通常是一种基于 gui 的应用程序,用于检查蛋白质结构。如果您运行 chimera --nogui ,它会打开一个提示并接受输入。

在运行下一个命令之前,我经常需要知道什么嵌合体输出。例如,我会经常尝试生成一个蛋白质表面,如果 Chimera 不能生成一个表面,它就不会破裂——它只是通过 STDOUT 这么说。因此,在我的 python 脚本中,当我循环分析许多蛋白质时,我需要检查 STDOUT 以了解是否继续对该蛋白质进行分析。

在其他用例中,我会先通过 Chimera 运行很多命令来清理蛋白质,然后我会想运行很多单独的命令来获取不同的数据,并使用这些数据来决定是否运行其他命令。我可以获取数据,关闭子进程,然后运行另一个进程,但这需要每次都重新运行所有这些清理命令。

无论如何,这些是我希望能够将 STDIN 推送到子进程、读取 STDOUT 并且仍然能够推送更多 STDIN 的一些现实原因。

谢谢你的时间!

最佳答案

你不需要使用 process.communicate在你的例子中。

只需使用 process.stdin.write 即可读写和 process.stdout.read .还要确保发送换行符,否则 read不会回来。当您从 stdin 读取时,您还必须处理来自 echo 的换行符。 .

注:process.stdout.read将阻塞直到 EOF .

# talk_with_example_input.py
import subprocess

process = subprocess.Popen(["./exampleInput.sh"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE)

process.stdin.write("5\n")
stdout = process.stdout.readline()
print(stdout)

if stdout == "25\n":
process.stdin.write("yes\n")
print(process.stdout.readline())

$ python2 test.py
25

Okay, moving on...



更新

当以这种方式与程序通信时,您必须特别注意应用程序实际编写的内容。最好是在十六进制编辑器中分析输出:
$ chimera --nogui 2>&1 | hexdump -C

请注意 readline [1] 只读取到下一个换行符 ( \n )。在您的情况下,您必须调用 readline至少四次才能获得第一个输出块。

如果您只想在子进程停止打印之前读取所有内容,则必须逐字节读取并实现超时。可悲的是,没有 read也不是 readline确实提供了这样的超时机制。这可能是因为底层 read syscall [2] (Linux) 也不提供。

在 Linux 上,我们可以编写一个单线程 read_with_timeout()使用 poll / select .有关示例,请参见 [3]。

from select import epoll, EPOLLIN

def read_with_timeout(fd, timeout__s):
"""Reads from fd until there is no new data for at least timeout__s seconds.

This only works on linux > 2.5.44.
"""
buf = []
e = epoll()
e.register(fd, EPOLLIN)
while True:
ret = e.poll(timeout__s)
if not ret or ret[0][1] is not EPOLLIN:
break
buf.append(
fd.read(1)
)
return ''.join(buf)

如果您需要一种可靠的方式在 Windows 和 Linux 下读取非阻塞, this answer might be helpful .

[1] 来自 python 2 docs :

readline(limit=-1)

Read and return one line from the stream. If limit is specified, at most limit bytes will be read.

The line terminator is always b'\n' for binary files; for text files, the newline argument to open() can be used to select the line terminator(s) recognized.



[2] 来自 man 2 read :

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);


[3] 例子
$ tree
.
├── prog.py
└── prog.sh

程序文件
#!/usr/bin/env bash

for i in $(seq 3); do
echo "${RANDOM}"
sleep 1
done

sleep 3
echo "${RANDOM}"

程序.py

# talk_with_example_input.py
import subprocess
from select import epoll, EPOLLIN

def read_with_timeout(fd, timeout__s):
"""Reads from f until there is no new data for at least timeout__s seconds.

This only works on linux > 2.5.44.
"""
buf = []
e = epoll()
e.register(fd, EPOLLIN)
while True:
ret = e.poll(timeout__s)
if not ret or ret[0][1] is not EPOLLIN:
break
buf.append(
fd.read(1)
)
return ''.join(buf)

process = subprocess.Popen(
["./prog.sh"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE
)

print(read_with_timeout(process.stdout, 1.5))
print('-----')
print(read_with_timeout(process.stdout, 3))

$ python2 prog.py 
6194
14508
11293

-----
10506


关于Python 子进程 : Giving stdin, 读取标准输出,然后提供更多标准输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57225646/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com