python学习记录001-subprocess命令使用

模块介绍

subprocess模块允许您生成新进程,连接到它们的输入/输出/错误管道,并获取它们的返回码。下面是官方文档里的原话。subprocess模块更新了一些函数的使用情景或语法要求,但也保留了一部分原来的函数,以保证向下兼容。也就是说,.exe 文件不需要主动运行,而是通过 python 脚本启动,然后获取参数的值。

The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.

使用情景

最近遇到一个需求,需要获取一个 .exe 文件的输出。
在网上找到的方法大概就是下面三种[1]

os.system()

import os 
main = "port_test.exe"
r_v = os.system(main) 
print (r_v )

commands.getstatusoutput()

import subprocess 
import os 
main = "port_test.exe"
if os.path.exists(main): 
  rc,out= subprocess.getstatusoutput(main) 
  print (rc)
  print ('*'*10)
  print (out)

popen()

import os
main = "port_test.exe"
f = os.popen(main)  
data = f.readlines()  
f.close()  
print (data)

上面的三种方法我尝试了多次,包括使用新旧方法,包括使用类似的包含 cmdshell = True 命令的函数,要么没有输出内容,程序一直运行,要么输出不明意义的乱码,可能某部分出了问题,但由于第一次使用,还没彻底弄明白模块的使用方式,所以没有深究下去。
后面找到了另一篇文章(参考文章2),可谓是帮了大忙。文章里强调了以下三点[2]

  1. 父进程不能有 shell = True 的参数
  2. 子进程使用 raw_input() 进行读取时,发送者必须加上 \n,也必须加上 flush
  3. 返回时,别用print(),用 sys.stdout.write('{}\n'format(str)) 这样好控制行数,sys.stdout.flush(),这样清空buffer,不让程序卡死。
    这里我没有用文章给出的例子,因此也没有完全按照文章强调的三个例子进行修改。我使用的 .exe 文件内容是:
# port_test.py 文件,在终端使用命令 pyinstaller -F test\port_test.py 转为 .exe 文件
import time
import sys


count = 0
while 1:
    sys.stdout.write('{}\n'.format(count))
    sys.stdout.flush()
    count += 1
    time.sleep(1)

这里得到的 .exe 文件开始运行后会每隔一秒在命令窗口打印一个随时间增长的自然数。那么需要获取的 .exe 文件的输出内容就是每隔一秒得到一个按顺序输出的自然数。
父进程 .py 文件按要求应该是

import subprocess


res = subprocess.Popen(r"D:\xxx\port_test.exe", stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)
while 1:
    print(res.stdout.readline().decode("utf-8"))

代码中的 \xxx 代表实际路径。因为需要不断输出,所以用到了一个循环。