pyinstaller打包成無控制臺(tái)程序時(shí)運(yùn)行出錯(cuò)(與popen沖突的解決方法)
有時(shí)候我們需要在程序里執(zhí)行一些cmd命令,使用os或者其它模塊中的popen方法去執(zhí)行
這個(gè)問題一般是程序內(nèi)有輸入導(dǎo)致的,這個(gè)輸入可以是input()
,也可以是其它的一些stdin操作(如os.popen實(shí)際上會(huì)造成輸入請(qǐng)求)
本質(zhì)上就是:使用-w
參數(shù)(無控制臺(tái))打包時(shí)程序里不要請(qǐng)求輸入
或者,你也可以不用-w
參數(shù),手動(dòng)隱藏控制臺(tái)!
有一天,我把使用了os.popen方法的python程序用pyinstaller打包成exe(用了無控制臺(tái)打包參數(shù)-w
)
雙擊運(yùn)行時(shí)程序卻彈框報(bào)錯(cuò)!
我就有點(diǎn)納悶:為什么有控制臺(tái)打包出來的exe(不使用-w
參數(shù))可以運(yùn)行,使用-w
參數(shù)(無控制臺(tái))打包的卻不能運(yùn)行呢?
首先,調(diào)用os.popen
部分的代碼大概是下面這樣的:
with os.popen('taskkill /f /t /im nginx.exe') as re: # 殺掉nginx result = re.read()
執(zhí)行cmd,殺死nginx。
經(jīng)過研究,上結(jié)論:
os.popen
會(huì)打開一個(gè)管道執(zhí)行命令,而管道是有輸入(stdin)、輸出(stdout) 的!
重點(diǎn)就在輸入(stdin)這里:
當(dāng)我們使用pyinstaller的-w
參數(shù)(或Console=False
)打包exe時(shí),python解釋器是不帶控制臺(tái)的,
所以它沒有辦法處理輸入(stdin) !
包括使用python的input()
函數(shù)也是不行的,都會(huì)彈框報(bào)錯(cuò)。
那么怎么辦呢?接著看!
os.popen
實(shí)際上是一個(gè)簡(jiǎn)單的封裝,我們先來看他的原型: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 )
簡(jiǎn)單的解釋一下(詳細(xì)請(qǐng)看官方文檔):
subprocess官方文檔:https://docs.python.org/2/library/subprocess.html
懶得看解釋可以直接跳過下面這段,直接看解決方法
args
是一個(gè)字符串(如cmd命令),或者是包含程序參數(shù)的列表。要執(zhí)行的程序一般就是這個(gè)列表的第一項(xiàng),或者是字符串本身。但是也可以用executable
參數(shù)來明確指出。當(dāng)executable
參數(shù)不為空時(shí),args
里的第一項(xiàng)被認(rèn)為是“命令名”,不同于真正的可執(zhí)行文件的文件名,這個(gè)“命令名”是一個(gè)用來顯示的名稱,例如執(zhí)行unix/linux下的 ps
命令,顯示出來的就是這個(gè)“命令名”。
bufsize
作用就跟python函數(shù)open()
的buffering
參數(shù)一樣:0表示不緩沖,1表示行緩沖,其他正數(shù)表示近似的緩沖區(qū)字節(jié)數(shù),負(fù)數(shù)表示使用系統(tǒng)默認(rèn)值。默認(rèn)是0。
executable
參數(shù)指定要執(zhí)行的程序。它很少會(huì)被用到,一般程序可以由args參數(shù)指定。如果shell
參數(shù)為True
,executable可以用于指定用哪個(gè)shell來執(zhí)行(比如bash、csh、zsh等)。windows下,只有當(dāng)你要執(zhí)行的命令是shell內(nèi)建命令(比如dir
,copy
等) 時(shí),你才需要指定shell=True
,而當(dāng)你要執(zhí)行一個(gè)基于命令行的批處理腳本(bat啥的)的時(shí)候,不需要指定此項(xiàng)。
stdin
、stdout
和stderr
分別表示子程序的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。 可選的值有PIPE
或者一個(gè)有效的文件描述符(其實(shí)是個(gè)正整數(shù))或者一個(gè)文件對(duì)象,還有None。如果是PIPE
,則表示需要?jiǎng)?chuàng)建一個(gè)新的管道,如果是 None
,不會(huì)做任何重定向工作,子進(jìn)程的文件描述符會(huì)繼承父進(jìn)程的。另外,stderr
的值還可以是STDOUT
,表示子進(jìn)程的標(biāo)準(zhǔn)錯(cuò)誤也輸出到標(biāo)準(zhǔn)輸出。
如果把preexec_fn
設(shè)置為一個(gè)可調(diào)用的對(duì)象(比如函數(shù)),就會(huì)在子進(jìn)程被執(zhí)行前被調(diào)用。(僅限unix/linux)
如果把close_fds
設(shè)置成True,unix/linux下會(huì)在開子進(jìn)程前把除了0、1、2以外的文件描述符都先關(guān)閉。在 Windows下也不會(huì)繼承其他文件描述符。
如果把shell
設(shè)置成True
,指定的命令會(huì)在shell
里解釋執(zhí)行,這個(gè)前面已經(jīng)說得比較詳細(xì)了。
如果cwd
(工作目錄)不是None
,則會(huì)把cwd做為子程序的當(dāng)前目錄。注意,并不會(huì)把該目錄做為可執(zhí)行文件的搜索目錄,所以不要把程序文件所在目錄設(shè)置為cwd。
如果env
不是None
,則子程序的環(huán)境變量由env的值來設(shè)置,而不是默認(rèn)那樣繼承父進(jìn)程的環(huán)境變量。注意,即使你只在env里定義了某一個(gè)環(huán)境變量的值,也會(huì)阻止子程序得到其他的父進(jìn)程的環(huán)境變量(也就是說,如果env里只有1項(xiàng),那么子進(jìn)程的環(huán)境變量就 只有1個(gè)了)。
如果把universal_newlines
設(shè)置成True
,則子進(jìn)程的stdout
和stderr
被視為文本對(duì)象,并且不管是unix/linux的換行符('\n'),還是老mac格式的換行符('\r'),還是windows 格式的換行符('\r\n')都將被視為'\n' 。
如果指定了startupinfo
和creationflags
,它們將會(huì)被傳遞給后面的CreateProcess()
函數(shù),用于指定子程序的各種其他屬性,比如主窗口樣式或者是子進(jìn)程的優(yōu)先級(jí)等。(僅限Windows)
再解釋一下兩個(gè)我們后面要用到的東西:
subprocess.PIPE
一個(gè)可以用于Popen的stdin
、stdout
或stderr
參數(shù)的特殊值,它指示應(yīng)打開到標(biāo)準(zhǔn)流的管道。
subprocess.STDOUT
一個(gè)可以被用于Popen的stderr
參數(shù)的特殊值,表示子程序的標(biāo)準(zhǔn)錯(cuò)誤與標(biāo)準(zhǔn)輸出匯合到同一句柄。
現(xiàn)在回到我們將要解決的問題
已知:
- 用pyinstaller的
-w
參數(shù)打包導(dǎo)致python無法處理輸入值(stdin) os.popen
打開的管道卻需要處理輸入值(stdin)
所以,我們不使用os.popen
這個(gè)簡(jiǎn)單的封裝,改成使用subprocess.popen
,接著將subprocess.popen
打開管道的輸入值(stdin)重定向,即可解決問題!
請(qǐng)看下列示例:
proc = subprocess.Popen( 'cmd命令', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE # 重定向輸入值 ) proc.stdin.close() # 既然沒有命令行窗口,那就關(guān)閉輸入 proc.wait() result = proc.stdout.read() # 讀取cmd執(zhí)行的輸出結(jié)果(是byte類型,需要decode) proc.stdout.close()
這樣處理后我們用-w
參數(shù)打包就不會(huì)再報(bào)錯(cuò)了!
也可以將輸出值(stdout)定向到文件輸出,請(qǐng)看:
with open('輸出文件.txt' , 'w+', encoding='utf-8') as out_file: proc = subprocess.Popen( 'cmd命令', shell=True, stdout=out_file, # 注意這里!變成了文件對(duì)象! stderr=subprocess.STDOUT, stdin=subprocess.PIPE ) ret = proc.wait() # 此處其實(shí)有返回值 with open('輸出文件.txt', 'r', encoding='utf-8' as read_file: output = read_file.read() # 這樣就得到cmd命令的輸出結(jié)果了
稍微封裝一下,就可以直接拿來用了
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)
舒服了?。。?!
當(dāng)然,實(shí)在要用輸入,又不想要控制臺(tái)怎么辦?很簡(jiǎn)單,把控制臺(tái)隱藏了就行!
下列兩個(gè)方法,試試看:
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)
到此這篇關(guān)于pyinstaller打包成無控制臺(tái)程序時(shí)運(yùn)行出錯(cuò)(與popen沖突的解決方法)的文章就介紹到這了,更多相關(guān)pyinstaller打包popen沖突內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 將日期戳(五位數(shù)時(shí)間)轉(zhuǎn)換為標(biāo)準(zhǔn)時(shí)間
這篇文章主要介紹了python 將日期戳(五位數(shù)時(shí)間)轉(zhuǎn)換為標(biāo)準(zhǔn)時(shí)間的實(shí)現(xiàn)方法,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-07-07如何在Python里使用ChatGPT及ChatGPT簡(jiǎn)介
今年最火的應(yīng)該就是ChatGPT了,現(xiàn)在短短2個(gè)月已經(jīng)突破了1億注冊(cè)用戶,馬斯克可高興壞了,這篇文章主要介紹了如何在Python里使用ChatGPT及ChatGPT是什么?注冊(cè)方式?需要的朋友可以參考下2023-02-02python如何用columns參數(shù)獲取DataFrame各列的表頭名
這篇文章主要介紹了python如何用columns參數(shù)獲取DataFrame各列的表頭名問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03Python從數(shù)據(jù)庫讀取大量數(shù)據(jù)批量寫入文件的方法
今天小編就為大家分享一篇Python從數(shù)據(jù)庫讀取大量數(shù)據(jù)批量寫入文件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-12-12pyCharm 設(shè)置調(diào)試輸出窗口中文顯示方式(字符碼轉(zhuǎn)換)
這篇文章主要介紹了pyCharm 設(shè)置調(diào)試輸出窗口中文顯示方式(字符碼轉(zhuǎn)換),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-06-06python之pyqt5通過按鈕改變Label的背景顏色方法
今天小編就為大家分享一篇python之pyqt5通過按鈕改變Label的背景顏色方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-06-06python導(dǎo)入csv文件出現(xiàn)SyntaxError問題分析
這篇文章主要介紹了python導(dǎo)入csv文件出現(xiàn)SyntaxError問題分析,同時(shí)涉及python導(dǎo)入csv文件的三種方法,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12