对付printf之后没有fflush(stdout)的程序

本作品采用 知识共享署名-非商业性使用-相同方式共享 3.0 Unported许可协议 进行许可。允许非商业转载,但应注明作者及出处。
作者:xialulee
最初发布于:2011年1月28日, http://blog.sina.com.cn/xialulee
       一个朋友愁眉苦脸的陪我喝茶。他抱怨道:有个已经远走高飞的家伙 留下了一个exe,这个exe里包含了某种算法,我们经常用它来处理数据,可是程序的作者没有留下源码,也没有考虑过可扩展性,比如做成dll之类的。因此这个exe只能以交互的方式使用,每次人工往里输入数据,很辛苦。
       我说:不能在Python中利用subprocess来调用这个exe,实现自动运转吗?
       他说:没有看过程序的源码,但是我猜作者一定是printf之后没有fflush(stdout),因此使用subprocess打开进程之后,从进程的stdin输入一行数据,没法从进程的stdout取得输出。阻塞了。
       我说:不能一次将所有要输入的数据全部输入,然后一次性将结果全部读出吗?使用subprocess的communicate可以做到,是比较推荐的方法。
       他说:可惜的是办不到。因为这个程序里用到了某种迭代算法,每次输入数据后,程序进行一次迭代,然后返回迭代的结果,我们将返回的结果进行一些运算,再将数据输入给程序,进行下一次迭代。也就是说第n次的输入与第n-1次的输出有关,所以无法一次性将所有数据输入,然后一次性取出全部结果。
       我说:可以使用pty吗?
       他说:很遗憾,是Windows的程序。
       我说:如果有源码就好了啊,在每一个printf后面加上fflush(stdout)就好了。
       他说:如果有源码,什么都好办。
       我说:让我们试验试验吧,说不定能够找到解决办法。
       于是我拿出笔记本,写了一个简单的C程序(increase.c):
#include <stdio.h>

int main(void){
    int i;
    while (1){
        scanf("%d"&i);
        if (i == -1)
            break;
        printf("%d\n"i+1);
        fflush(stdout);
    }
}


使用MinGW的gcc编译:
xialulee@xialulee-pc3 ~/lab
$ gcc increase.c -o increase.exe
就可以用了。
       这个试验程序很简单,我们输入1,它就输出2,输入2,输出3,即将输入加1。现在我们试试在Python中使用subprocess和increase.exe进行双向通信:
[e:xialulee/lab]|7> import subprocess as sp
[e:xialulee/lab]|8> p = sp.Popen(['increase'], stdin=sp.PIPE, stdout=sp.PIPE)
[e:xialulee/lab]|9> p.stdin.write('1\n')
[e:xialulee/lab]|10> p.stdout.readline()
                               <10> '2\r\n'
[e:xialulee/lab]|11> p.stdin.write('2\n')
[e:xialulee/lab]|12> p.stdout.readline()
                               <12> '3\r\n'
很正常。
       现在修改一下increase.c,将fflush(stdout)注释掉,即
#include <stdio.h>

int main(void){
    int i;
    while (1){
        scanf("%d"&i);
        if (i == -1)
            break;
        printf("%d\n"i+1);
        //fflush(stdout);
    }
}

重新用gcc编译一遍。再用subprocess试试:
[e:xialulee/lab]|14> p = sp.Popen(['increase'], stdin=sp.PIPE, stdout=sp.PIPE)
[e:xialulee/lab]|15> p.stdin.write('1\n')
[e:xialulee/lab]|16> p.stdout.readline()
p.stdout.readline()没有返回。
       他说:是吧。
       我说:想起来了。PP3E上说过这个问题,说一个程序如果不是以交互的方式运行的话,那么stdout这些都是完全缓冲的,即使设置bufsize也没用,可行的办法是使用pty或者在源码中使用fflush强制flush stdout。
       他说:可是无论是使用pty还是修改源码对于我来说都是无法实现的。
       我说:我突然想到了一个邪恶的主意。我们可以把这个Windows的程序放到Linux中,使用Wine来运行,这样不就可以了吗?
       由于现在身边只有这个笔记本,于是打开虚拟机,运行Ubuntu。将increase.exe拷贝到共享目录,由于Wine已经装好了,所以应该直接就可以运行:
xialulee@xialulee-pc3:/mnt/rootmachine$ ./increase.exe
fixme:msvcrt:_setmbcp trail bytes data not available for DBCS codepage 0 - assuming all bytes
1
2
100
101
-1
虽然输出了一行诡异的错误信息,但程序的运行是正常的。
       他说:你的这个exe可以在Ubuntu下用Wine运行。我的那个也应该可以,因为只是实现了算法,并没有使用什么特殊的系统功能。那么现在你可以展示一下如何使用Python的pty来搞定它了。
       我说:比较抱歉的是……我不会在Python中用pty……
       他说:……
       我说:等等,好像gawk也可用pty,而且很简单。
       打开 GAWK: Effective AWK Programming ,找到如下的一句话:
Beginning with gawk 3.1.2, you may use Pseudo-ttys (ptys) for two-way communication instead of pipes, if your system supports them.
而使用方法则极为简单,只需:
command = "sort -nr"
PROCINFO[command, "pty"] = 1
就可以了。
       我说:试试用gawk吧,如果gawk能够实现双向通信,Python也一定可以的。
       于是写了如下的一段代码(gawkpty),这段gawk代码会启动increase.exe,并将1和100两个数字传递给它,如果运行无误的话,我们会得到2和101的输出:
#!/usr/bin/gawk -f

# 2011.01.28 PM 04:28
# xialulee

BEGIN{
  INC "./increase.exe 2>/dev/null"
  print |& INC
  INC |& getline
  print
  print 100 |& INC
  INC |& getline
  print
  print -1 |& INC
  close(INC)
}


运行:
xialulee@xialulee-pc3:/mnt/rootmachine$ vim gawkpty
xialulee@xialulee-pc3:/mnt/rootmachine$ ./gawkpty
2
101
果然正常运行了。
       他说: 不对!有个大问题!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值