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

朱莉安斯坦利

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

我想调用一个流程,为该流程提供一些输入,读取其输出,并基于该输入为其提供更多输入,等等。

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


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

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

#!/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”。

我想在其中传递exampleInput.sh“ 5”的地方运行一个python脚本,如果返回“ 25”,那么我传递“是”

到目前为止,这与我所能得到的差不多:

#!/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提供这样的超时机制。这可能是因为底层的readsyscall [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下的非阻塞,则此答案可能会有所帮助


[1]来自python 2文档

readline(限制= -1)

从流中读取并返回一行。如果指定了限制,则最多将读取限制字节。

对于二进制文件,行终止符始终为b'\ n';对于文本文件,open()的newline参数可用于选择识别的行终止符。

[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}"

程序

# 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


本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何“抓”标准输入,标准输出,在Python程序的标准错误?

从标准输入读取粘贴命令

如何从标准输入逐行读取?

Linux 3.0:使用管道标准输入/标准输出执行子进程

从标准输入读取密码

从标准输入读取整数

如何将字符串推入标准输入?在启动时通过stdin提供输入,然后以交互方式读取stdin输入

关闭标准输出或标准输入

从标准输入Bash逐行读取

读取正在等待输入的生成进程的标准输出

写入子进程的标准输入

记录标准输入和标准输出

记录标准输入和标准输出

Python从标准输入读取XML

如何在仍在运行的标准输入中写入进程标准输入并从其标准输出中读取?

传递文件或标准输入来模糊处理子进程

Python:如何写入子流程的标准输入并实时读取其输出

提供标准输入以通过文件进行编程,并将标准输出带入文件

X86从标准输入读取并写入标准输出,而无需引用标准库

Python子进程/ Popen标准输出被截断

如何从标准输入中读取进程替换?

Python文件处理:标准输入和输出

子流程标准输入

重定向标准输入和标准输出?

阅读并打印“标准输入和标准输出”

如何将输入发送到使用命令创建的进程的标准输入,然后捕获输出(和状态)

检测子进程何时等待标准输入

从线程中的子进程标准输出读取时的随机行为

如何从bash中的子进程实时读取标准输出