pyinstaller打包成无控制台程序时运行出错(与popen冲突的解决方法)

有时候我们需要在程序里执行一些cmd命令,使用os或者其它模块中的popen方法去执行

这个问题一般是程序内有输入导致的,这个输入可以是input(),也可以是其它的一些stdin操作(如os.popen实际上会造成输入请求)

本质上就是:使用-w参数(无控制台)打包时程序里不要请求输入

或者,你也可以不用-w参数,手动隐藏控制台!

有一天,我把使用了os.popen方法的python程序用pyinstaller打包成exe(用了无控制台打包参数-w
双击运行时程序却弹框报错!

我就有点纳闷:为什么有控制台打包出来的exe(不使用-w参数)可以运行,使用-w参数(无控制台)打包的却不能运行呢?

首先,调用os.popen部分的代码大概是下面这样的:

with os.popen('taskkill /f /t /im nginx.exe') as re: # 杀掉nginx
  result = re.read()

执行cmd,杀死nginx。

经过研究,上结论:

os.popen 会打开一个管道执行命令,而管道是有输入(stdin)、输出(stdout) 的!

重点就在输入(stdin)这里:

当我们使用pyinstaller的-w 参数(或Console=False)打包exe时,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内建命令(比如dircopy等) 时,你才需要指定shell=True,而当你要执行一个基于命令行的批处理脚本(bat啥的)的时候,不需要指定此项。

stdinstdoutstderr分别表示子程序的标准输入、标准输出和标准错误。 可选的值有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,则子进程的stdoutstderr被视为文本对象,并且不管是unix/linux的换行符('\n'),还是老mac格式的换行符('\r'),还是windows 格式的换行符('\r\n')都将被视为'\n' 。

如果指定了startupinfocreationflags,它们将会被传递给后面的CreateProcess()函数,用于指定子程序的各种其他属性,比如主窗口样式或者是子进程的优先级等。(仅限Windows)

再解释一下两个我们后面要用到的东西:

subprocess.PIPE
一个可以用于Popen的stdinstdoutstderr参数的特殊值,它指示应打开到标准流的管道。

subprocess.STDOUT
一个可以被用于Popen的stderr参数的特殊值,表示子程序的标准错误与标准输出汇合到同一句柄。

现在回到我们将要解决的问题

已知:

  • 用pyinstaller的-w参数打包导致python无法处理输入值(stdin)
  • os.popen 打开的管道却需要处理输入值(stdin)

所以,我们不使用os.popen这个简单的封装,改成使用subprocess.popen,接着将subprocess.popen打开管道的输入值(stdin)重定向,即可解决问题!

请看下列示例:

proc = subprocess.Popen(
  'cmd命令',
  shell=True,
  stdout=subprocess.PIPE,
  stderr=subprocess.STDOUT,
  stdin=subprocess.PIPE # 重定向输入值
)
proc.stdin.close() # 既然没有命令行窗口,那就关闭输入
proc.wait()
result = proc.stdout.read() # 读取cmd执行的输出结果(是byte类型,需要decode)
proc.stdout.close()

这样处理后我们用-w参数打包就不会再报错了!

也可以将输出值(stdout)定向到文件输出,请看:

with open('输出文件.txt' , 'w+', encoding='utf-8') as out_file:
  proc = subprocess.Popen(
	  'cmd命令',
	  shell=True,
	  stdout=out_file, # 注意这里!变成了文件对象!
	  stderr=subprocess.STDOUT,
	  stdin=subprocess.PIPE
	)
	ret = proc.wait() # 此处其实有返回值
with open('输出文件.txt', 'r', encoding='utf-8' as read_file:
  output = read_file.read() # 这样就得到cmd命令的输出结果了

稍微封装一下,就可以直接拿来用了

def execute_cmd(cmd):
	proc = subprocess.Popen(
	  cmd,
	  shell=True,
	  stdout=subprocess.PIPE,
	  stderr=subprocess.STDOUT,
	  stdin=subprocess.PIPE
	)
	proc.stdin.close()
	proc.wait()
	result = proc.stdout.read().decode('gbk') # 注意你电脑cmd的输出编码(中文是gbk)
	proc.stdout.close()
	return result

result = execute_cmd('taskkill /f /t /im nginx.exe')
print(result)

舒服了!!!!

当然,实在要用输入,又不想要控制台怎么办?很简单,把控制台隐藏了就行!

下列两个方法,试试看:

import ctypes
def hideConsole():
  """
  Hides the console window in GUI mode. Necessary for frozen application, because
  this application support both, command line processing AND GUI mode and theirfor
  cannot be run via pythonw.exe.
  """

  whnd = ctypes.windll.kernel32.GetConsoleWindow()
  if whnd != 0:
    ctypes.windll.user32.ShowWindow(whnd, 0)
    # if you wanted to close the handles...
    #ctypes.windll.kernel32.CloseHandle(whnd)

def showConsole():
  """Unhides console window"""
  whnd = ctypes.windll.kernel32.GetConsoleWindow()
  if whnd != 0:
    ctypes.windll.user32.ShowWindow(whnd, 1)

到此这篇关于pyinstaller打包成无控制台程序时运行出错(与popen冲突的解决方法)的文章就介绍到这了,更多相关pyinstaller打包popen冲突内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python3.8与pyinstaller冲突问题的快速解决方法

    安装pyinstaller 安装的时候 进入cmd pip install pyinstaller 发现安装报错! 解决办法: # 自主下载pyinstaller包,进行手动安装 pyinstaller 的下载地址:http://www.pyinstaller.org/downloads.html 下载文件后,解压文件. cmd 进入解压的路径 输入: python setup.py install 等待一段时间,pyinstaller就会安装成功 Python项目生成可执行程序的步骤 选择一:

  • pyinstaller打包成无控制台程序时运行出错(与popen冲突的解决方法)

    有时候我们需要在程序里执行一些cmd命令,使用os或者其它模块中的popen方法去执行 这个问题一般是程序内有输入导致的,这个输入可以是input(),也可以是其它的一些stdin操作(如os.popen实际上会造成输入请求) 本质上就是:使用-w参数(无控制台)打包时程序里不要请求输入 或者,你也可以不用-w参数,手动隐藏控制台! 有一天,我把使用了os.popen方法的python程序用pyinstaller打包成exe(用了无控制台打包参数-w) 双击运行时程序却弹框报错! 我就有点纳闷:

  • Visual Studio Code运行程序时输出中文成乱码问题及解决方法

    今天写代码,需要输出一些中文,于是就顺势发现了这个问题:VS Code输出中文成乱码.上网查询了一番后,我找到了解决方法,我决定将我看到的方法整理出来,帮助更多朋友.(windows10系统下) 1. 打开控制面板 可以点击电脑桌面上的相应图标打开 也可以点击桌面左下角开始按钮,找到W开头文件列表下的windows系统文件夹中的控制面板 2.选择时钟和区域下的更改日期.时间或数字格式 3.点击管理,然后点选择更改系统区域设置 4. 勾选Beta版:使用Unicode UTF-8提供全球语言支持,

  • 如何将Python脚本打包成exe应用程序介绍

    目录 前言 安装Pyinstaller 将脚本打包成exe应用程序 进阶:消除命令窗口.自定义图标 总结 前言 我们有时候会编写Python脚本来辅助我们执行一些重复的操作.但是这些脚本在实际使用中会有一些不方便: 我们通常需要进入终端或者IDE中来运行脚本(当然,有办法可以实现双击脚本文件直接运行,但这不在今天的讨论范围内). 如果把脚本迁移至其他电脑上,那么Python环境变化,比如新电脑上没有安装Python,或者缺乏该脚本所调用的包等,脚本有很大概率无法正常运行. 这时候,我们可以将脚本

  • pyinstaller打包opencv和numpy程序运行错误解决

    前言 这篇文章主要介绍了pyinstaller打包opencv和numpy程序运行错误解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 报错背景 这两天公司的程序许需要打包,就开始又操作了一番. pyinstller 打包含有opencv+numpy库 打包成功,但是运行报错.在排除外部依赖文件因素外,看了一下运行结果. ImportError: numpy.core.multiarray failed to import 解决方案 重要的是

  • 将python文件打包成EXE应用程序的方法

    相信大家都想把自己完成的项目打包成EXE应用文件,然后就可以放在桌面随时都能运行了,下面来分享利用pytinstaller这个第三方库来打包程序,既简单又快捷,我也试过用其他的方式来打包Python文件,但是都没有pyinstaller这个好用和快捷 首先我将详细的将整个操作过程写出来,你首先要安装pycharm,这个无脑的操作,就不必说了,(看完一定能学会) 打开pycharm的终端terminal,然后pip install pyinstaller(任何库都可以) 然后会出现安装日志 因为我

  • IDEA导出jar打包成exe应用程序的小结

    Java jar打包成exe应用程序,可在无JDK/JRE环境下运行 老师让做一个小项目,但是需要打包发布出来,因此在网上查了很多资料之后总结的经验. 1. IDEA导出jar包,选择 File -> Project Structure (快捷键:Ctrl+Alt+Shift+S). 2. 选中"Artifacts",点击"+"选择jar,然后选择"from modules with dependencies". 3. 选择文件图标,选中入

  • Java jar打包成exe应用程序的详细步骤

    Java jar打包成exe应用程序,可在无JDK/JRE环境下运行 前言 近期做了一个前后端合并的spring boot项目,但是要求达成exe文件,提供给不懂电脑的小白安装使用,就去研究了半天,踩了很多坑,写这篇文章,是想看到这篇文章的人,按照我的步骤走,能少踩坑. 准备 准备工作: 一个jar包,没有bug能正常启动的jar包 exe4j,一个将jar转换成exe的工具,链接:https://www.jb51.net/softs/541579.html 一个将依赖和exe一起打成一个安装程

  • Linux程序运行时加载动态库失败的解决方法

    Linux下不能加载动态库问题 当出现下边异常情况 ./test: error while loading shared libraries: libmfs_open.so: cannot open shared object file: No such file or directory 若动态库的路径在(/usr/cluster/.share/lib) 解决办法: 方法一.在/etc/ld.so.conf文件中添加路径,vi /etc/ld.so.conf 添加下边内容 include ld

  • uniapp打包成微信小程序的详细过程

    目录 一.HbuilderX打包 二.发行 三.打开小程序体验 注意 总结 一.HbuilderX打包 选中项目-点击发行(U)- 小程序-(微信仅适用于uniapp)(W) 二.发行 填写微信小程序Appid 点击发行,项目会进行编译,等待编译完成,会提示前往小程序上传 前往小程序开发工具打开这个小程序 然后提示上传成功 三.打开小程序体验 这是你的第一个版本,点击蓝色的体验,会有一个二维码,用自己的账号体验,让别人体验的话,要先把对方加入到开发者中. 接下来就是根据提示,填写信息,然后等待审

  • Hadoop运行时遇到java.io.FileNotFoundException错误的解决方法

    报错信息: java.lang.Exception: org.apache.hadoop.mapreduce.task.reduce.Shuffle$ShuffleError: error in shuffle in localfetcher#1 at org.apache.hadoop.mapred.LocalJobRunner$Job.runTasks(LocalJobRunner.java:462) at org.apache.hadoop.mapred.LocalJobRunner$Jo

随机推荐