gpt4 book ai didi

c++ - 子进程的异步双向 IO 重定向

转载 作者:IT王子 更新时间:2023-10-29 00:36:47 27 4
gpt4 key购买 nike

我正在尝试为子进程的异步双向 IO 重定向找出一种通用方法。基本上,我想生成一个等待输入的交互式子进程,并且应该回读任何输出。我试图通过生成一个新的 python 进程来试验 python.subprocess。尝试实现的基本简单示例如下

process = subprocess.Popen(['/usr/bin/python'],shell=False,stdin=subprocess.PIPE, stdout=subprocess.PIPE)
while True:
output = process.stdout.readline()
print output
input = sys.stdin.readline()
process.stdin.write(input)

并且执行上面的代码片段只是挂起而没有任何输出。我尝试使用 /usr/bash/usr/bin/irb 运行,但结果都是一样的。我的猜测是,缓冲 IO 根本无法与 IO 重定向很好地融合。

所以我的问题是,在不刷新缓冲区或退出子进程的情况下读取子进程的输出是否可行?

following post提到了 IPC 套接字,但为此我将不得不更改可能不可行的子进程。还有其他方法可以实现吗?

注意*** 我的最终目标是创建一个可以与远程 Web 客户端交互的服务器 REPL 进程。虽然给出的示例是 Python,但我的最终目标是通过通用包装器包装所有可用的 REPL。


在答案中的一些建议的帮助下,我得出了以下结论

#!/usr/bin/python
import subprocess, os, select
proc = subprocess.Popen(['/usr/bin/python'],shell=False,stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
for i in xrange(0,5):
inputready, outputready, exceptready = select.select([proc.stdout, proc.stderr],[proc.stdout, proc.stderr],[proc.stdout, proc.stderr],0)
if not inputready: print "No Data",
print inputready, outputready, exceptready
for s in inputready: print s.fileno(),s.readline()
proc.terminate()
print "After Terminating"
for i in xrange(0,5):
inputready, outputready, exceptready = select.select([proc.stdout, proc.stderr],[proc.stdout, proc.stderr],[proc.stdout, proc.stderr],0)
if not inputready: print "No Data",
print inputready, outputready, exceptready
for s in inputready: print s.fileno(),s.readline()

现在,虽然程序没有死锁,但不幸的是没有输出。运行上面的代码我得到

No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
After Terminating
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []

仅供引用,运行 python 作为

/usr/bin/python 2>&1|tee test.out

似乎工作得很好。

我还想出了一个“C”代码。但结果并没有什么不同。

int kbhit() {
struct timeval tv;
fd_set fds;
tv.tv_sec = tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
void receive(char *str) {
char ch;
fprintf(stderr,"IN1\n");
if(!kbhit()) return;
fprintf(stderr,"IN2\n");
fprintf(stderr,"%d\n",kbhit());
for(;kbhit() && (ch=fgetc(stdin))!=EOF;) {
fprintf(stderr,"%c,%d",ch,kbhit());
}
fprintf(stderr,"Done\n");
}
int main(){
pid_t pid;
int rv, pipeP2C[2],pipeC2P[2];
pipe(pipeP2C);
pipe(pipeC2P);
pid=fork();
if(pid){
dup2(pipeP2C[1],1); /* Replace stdout with out side of the pipe */
close(pipeP2C[0]); /* Close unused side of pipe (in side) */
dup2(pipeC2P[0],0); /* Replace stdin with in side of the pipe */
close(pipeC2P[1]); /* Close unused side of pipe (out side) */
setvbuf(stdout,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdout */
sleep(2);
receive("quit()\n");
wait(&rv); /* Wait for child process to end */
fprintf(stderr,"Child exited with a %d value\n",rv);
}
else{
dup2(pipeP2C[0],0); /* Replace stdin with the in side of the pipe */
close(pipeP2C[1]); /* Close unused side of pipe (out side) */
dup2(pipeC2P[1],1); /* Replace stdout with the out side of the pipe */
close(pipeC2P[0]); /* Close unused side of pipe (out side) */
setvbuf(stdout,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdout */
close(2), dup2(1,2); /*Redirect stderr to stdout */
if(execl("/usr/bin/python","/usr/bin/python",NULL) == -1){
fprintf(stderr,"execl Error!");
exit(1);
}
}
return 0;
}

最佳答案

在您发布的 Python 代码中,您没有使用正确的流:

inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], # read list
[proc.stdout, proc.stderr], # write list
[proc.stdout, proc.stderr], # error list.
0) # time out.

我没有尝试修复它,但我敢打赌读取和写入同一组流是不正确的。


您的示例中存在多处错误。首先是作为子进程启动的 python 可执行文件不产生任何输出。第二个是存在竞争条件,因为您可以在子进程产生输出之前连续调用 5 次 select(),在这种情况下,您将在读取任何内容之前终止该进程。

我修复了上面提到的三个问题(写入列表、启动一个产生输出和竞争条件的进程)。试试这个示例,看看它是否适合您:

#!/usr/bin/python
import subprocess, os, select, time

path = "/usr/bin/python"
proc = subprocess.Popen([path, "foo.py"], shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for i in xrange(0,5):
time.sleep(1)
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
if not inputready:
print "No Data",
print inputready, outputready, exceptready
for s in inputready:
print s.fileno(),s.readline()

proc.terminate()
print "After Terminating"

for i in xrange(0,5):
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
if not inputready:
print "No Data",
print inputready, outputready, exceptready
for s in inputready:
print s.fileno(),s.readline()

我使用的 foo.py 文件包含以下内容:

#!/usr/bin/python
print "Hello, world!"

以下版本(大部分删除了冗余输出以使结果更易于阅读):

#!/usr/bin/python
import subprocess, os, select, time

path = "/usr/bin/python"
proc = subprocess.Popen([path, "foo.py"], shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for i in xrange(0,5):
time.sleep(1)
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
for s in inputready:
line = s.readline()
if line:
print s.fileno(), line

proc.terminate()
print "After Terminating"

for i in xrange(0,5):
time.sleep(1)
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
for s in inputready:
line = s.readline()
if line:
print s.fileno(), line

给出以下输出:

5 Hello, world!

After Terminating

请注意,出于某种原因,在 select.select() 中使用 timeout 参数并没有在我的系统上产生预期的结果,我求助于使用 time.sleep() 代替。


Just FYI, running python as

/usr/bin/python 2>&1|tee test.out

seems to be working just fine.

你不能得到这个效果,因为这个例子仍然给了 python 解释器一个控制 tty。没有控制 tty,python 解释器不会打印 Python 版本,也不会显示 >>> 提示符。

一个接近的例子如下所示。您可以将 /dev/null 替换为包含要发送给解释器的命令的文件。

/usr/bin/python </dev/null 2>&1|tee test.out

如果您将控制 tty(键盘)以外的任何重定向为进程的标准输入,您将不会从 python 解释器获得任何输出。这就是您的代码似乎不起作用的原因。

关于c++ - 子进程的异步双向 IO 重定向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9550338/

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