原因
popen
会打开一个管道执行命令,而管道是有输入(stdin)、输出(stdout)的,但当我们使用pyinstaller打包可执行文件时使用了-w
参数或者是.spec文件中console=False
,python解释器是不带控制台的,所以它没有办法处理输入(stdin)
包括使用python的input()
函数都不行
os.popen
实际上是一个简单的封装,原型是subprocess.popen
subprocess.Popen(
args,
bufsize=0,
executable=None,
stdin=None,
stdout=None,
stderr=None,
preexec_fn=None,
close_fds=False,
shell=False,
cwd=None,
env=None,
universal_newlines=False,
startupinfo=None,
creationflags=0
)
简单的解释一下(详细请看官方文档):
subprocess官方文档:https://docs.python.org/2/library/subprocess.html
懒得看解释可以直接跳过下面这段,直接看解决方法
args
是一个字符串(如cmd命令),或者是包含程序参数的列表。要执行的程序一般就是这个列表的第一项,或者是字符串本身。但是也可以用executable
参数来明确指出。当executable
参数不为空时,args
里的第一项被认为是“命令名”,不同于真正的可执行文件的文件名,这个“命令名”是一个用来显示的名称,例如执行unix/linux下的 ps
命令,显示出来的就是这个“命令名”。
bufsize
作用就跟python函数open()
的buffering
参数一样:0表示不缓冲,1表示行缓冲,其他正数表示近似的缓冲区字节数,负数表示使用系统默认值。默认是0。
executable
参数指定要执行的程序。它很少会被用到,一般程序可以由args参数指定。如果shell
参数为True
,executable可以用于指定用哪个shell来执行(比如bash、csh、zsh等)。windows下,只有当你要执行的命令是shell内建命令(比如dir
,copy
等) 时,你才需要指定shell=True
,而当你要执行一个基于命令行的批处理脚本(bat啥的)的时候,不需要指定此项。
stdin
、stdout
和stderr
分别表示子程序的标准输入、标准输出和标准错误。 可选的值有PIPE
或者一个有效的文件描述符(其实是个正整数)或者一个文件对象,还有None。如果是PIPE
,则表示需要创建一个新的管道,如果是 None
,不会做任何重定向工作,子进程的文件描述符会继承父进程的。另外,stderr
的值还可以是STDOUT
,表示子进程的标准错误也输出到标准输出。
如果把preexec_fn
设置为一个可调用的对象(比如函数),就会在子进程被执行前被调用。(仅限unix/linux)
如果把close_fds
设置成True,unix/linux下会在开子进程前把除了0、1、2以外的文件描述符都先关闭。在 Windows下也不会继承其他文件描述符。
如果把shell
设置成True
,指定的命令会在shell
里解释执行,这个前面已经说得比较详细了。
如果cwd
(工作目录)不是None
,则会把cwd做为子程序的当前目录。注意,并不会把该目录做为可执行文件的搜索目录,所以不要把程序文件所在目录设置为cwd。
如果env
不是None
,则子程序的环境变量由env的值来设置,而不是默认那样继承父进程的环境变量。注意,即使你只在env里定义了某一个环境变量的值,也会阻止子程序得到其他的父进程的环境变量(也就是说,如果env里只有1项,那么子进程的环境变量就 只有1个了)。
如果把universal_newlines
设置成True
,则子进程的stdout
和stderr
被视为文本对象,并且不管是unix/linux的换行符('n'),还是老mac格式的换行符('r'),还是windows 格式的换行符('rn')都将被视为'n' 。
如果指定了startupinfo
和creationflags
,它们将会被传递给后面的CreateProcess()
函数,用于指定子程序的各种其他属性,比如主窗口样式或者是子进程的优先级等。(仅限Windows)
再解释一下两个我们后面要用到的东西:
subprocess.PIPE
一个可以用于Popen的stdin
、stdout
或stderr
参数的特殊值,它指示应打开到标准流的管道。
subprocess.STDOUT
一个可以被用于Popen的stderr
参数的特殊值,表示子程序的标准错误与标准输出汇合到同一句柄。
解决办法
首先问题根源:
- 用pyinstaller的
-w
参数打包导致python无法处理输入值(stdin) os.popen
打开的管道却需要处理输入值(stdin)
所以,我们不使用os.popen
这个简单的封装,改成使用subprocess.popen
,接着将subprocess.popen
打开管道的输入值(stdin)重定向,即可解决问题!
实例:
p = subprocess.Popen(
'cmd命令',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE, # 重定向输入值
creationflags=0x08000000 # 不弹窗
)
result = p.stdout.read().decode('gbk') # 读取cmd执行的输出结果
评论 (0)