【问题标题】:Python <p = Popen(["command"], stdout=PIPE)> when p.stdout.read(-1) is blockedPython <p = Popen(["command"], stdout=PIPE)> 当 p.stdout.read(-1) 被阻塞时
【发布时间】:2012-05-23 08:19:44
【问题描述】:

你好,我对 Python 子进程模块有疑问

我想与子进程交互(例如:子进程的标准输入、标准输出)

这是子程序的代码

  1 #include <stdio.h>
  2 
  3 int main(){
  4         char buf[1024];
  5         printf("asdfsadfasff\n");
  6         scanf("%s",buf);
  7         printf("%s\n",buf);
  8 }

这段代码很简单,打印字符串,等待输入,打印输入字符串

接下来是python代码

  1 #!/usr/bin/python
  2 from subprocess import *
  3 
  4 fd = Popen(["./random"],stdin=PIPE,stdout=PIPE)
  5 print "pid = %d"%(fd.pid) # print pid
  6 result = ""
  7 result = fd.stdout.read(-1) # read
  8 print result
  9 fd.stdin.write("write write write!!!\n")
 10 result = fd.stdout.read(-1)
 11 print result

在这种情况下,我预计这个程序会运行良好,但在第 7 行(fd.stdout.read(-1) 被阻止并且无法运行

我更改了读取函数的参数 (read(-1), read(1), read(), read(1024)) 但一切都不起作用

但是,当我首先在标准输入中输入字符串时,程序工作了。

我认为程序结束时孩子的标准输出缓冲区没有填满......这只是我的选择

这个问题有解决办法吗?

编辑1。 当我首先执行打印字符串的程序时,等待用户输入,例如“sudo su” 效果出乎我的意料

我不明白为什么这个案例运作良好

  1 from subprocess import *
  2 
  3 fd = Popen(["sudo","su"],stdin=PIPE,stdout=PIPE)
  4 
  5 print fd.stdout.read(-1)
  6 print fd.stdin.write("asdfasdfas\n")

【问题讨论】:

  • 如果您在第一个printf 之后flush(stdout); 会发生什么?
  • 显然C函数是fflush...对不起,我的C有点生锈了。
  • 1 #include 2 3 int main(){ 4 char buf[10]; 5 printf("asdfsadfasff\n"); 6 fflush(标准输出); 7 scanf("%s",buf); 8 printf("%s\n",buf); 9 } 添加fflush,结果没有改变,是不是调用fflush的时间有问题?
  • 哦,我的...抱歉缩进和换行不明确
  • @JoonSunKim 没有行号,它仍然是有效的 C 代码 ;)。不过看起来和我预想的一样。您还传递了一个对于您的字符缓冲区(stackoverflow.com/a/2430310/748858)来说太长的字符串。这也可能导致一些有趣的行为。

标签: python subprocess stdout popen


【解决方案1】:

子进程./random 正在使用stdio,当进程的标准输出不是 tty 设备时,它默认执行“全缓冲”而不是“行缓冲”。由于 python 通过管道(不是 tty 设备)运行进程,因此程序正在缓冲其输出,而不是在 scanf 调用时打印它。缓冲区仅在 (a) 填满时刷新(写入标准输出); (b) 当使用fflush 调用显式刷新时;或 (c) 在程序退出时。

您可以修改子进程以进行显式 fflush 调用,或者您可以使用 Python 中的伪 tty,例如,使用 pexpectsh。 (请注意,sh 模块默认情况下仅使用 pty 输出,但这通常就足够了。)或者,在这种情况下,您正在运行的程序仅“看起来是交互式的”(但实际上是完全可预测的),您可以使用预先提供的已知输入序列运行​​它:

fd = Popen(["./random"], stdin=PIPE, stdout=PIPE)
result = fd.communicate("write write write!!!\n")[0]

当然,您会将所有标准输出输入混合在一起,并且必须将它打印的两行分开(在这种特殊情况下)。

【讨论】:

  • 非常感谢但是在我的情况下,scanf 在获得父级中的第一个输出字符串之前从未执行过,(我的问题 => 程序随机生成种子,并使用此种子获取随机数(20 整数) 并打印出来。然后等待得到一些数字,然后检查输入和生成的种子号是否相同)在这种情况下,我理解完全缓冲系统可以解决问题......
  • 是否有任何标志或东西可以将shell的stdio缓冲系统公牛缓冲更改为行缓冲?
  • 一般来说没有办法改变一些其他程序的缓冲行为。但是,如果您使用pexpect(请参阅答案中的链接),您可以与假定它正在与用户交谈的程序进行交互,因为pexpect 打开一个伪tty,就像任何普通的交互式用户命令窗口。也就是说,您不会以任何方式更改子进程,您只是在实际的交互式会话下运行它。
  • 你能看到Edit1中的更多问题吗?当我执行 'sudo su' 为什么效果很好?
  • 重新编辑:sudo 打开/dev/tty 以访问运行命令的人,即使存在 I/O 重定向。 (换句话说,它试图根本不从stdin 读取密码。)但是,在我输入密码后,你的示例仍然对我来说是挂起的。
猜你喜欢
  • 2021-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 2013-06-04
  • 2019-05-08
  • 1970-01-01
  • 2017-12-10
相关资源
最近更新 更多