已經知道,os.system可以方便的利用python代碼執行一些像ping、ipconfig之類的系統命令,但卻只能得到命令執行是否成功,不能獲得命令成功執行后的結果,像下面這樣:
1 >>> s = os.system("ping www.baidu.com") 2 3 正在 Ping www.a.shifen.com [220.181.38.150] 具有 32 字節的數據: 4 來自 220.181.38.150 的回復: 字節=32 時間=18ms TTL=52 5 來自 220.181.38.150 的回復: 字節=32 時間=19ms TTL=52 6 來自 220.181.38.150 的回復: 字節=32 時間=23ms TTL=52 7 來自 220.181.38.150 的回復: 字節=32 時間=22ms TTL=52 8 9 220.181.38.150 的 Ping 統計信息: 10 數據包: 已發送 = 4,已接收 = 4,丟失 = 0 (0% 丟失), 11 往返行程的估計時間(以毫秒為單位): 12 最短 = 18ms,最長 = 23ms,平均 = 20ms 13 >>> s 14 0 15 >>> type(s) 16 <class 'int'> 17 >>>
在上面的代碼中,利用os.system執行“ping www.baidu.com”並把結果賦值給s,但在下面可以看到,s的內容是int類型的0(表示命令執行成功),並不是命令的執行結果。如果只是需要判斷命令是否執行成功,那完全可以使用這種方法,但如果想要獲取命令執行的結果呢?可以使用subprocess這個模塊。
一:subprocess的作用
subprocess模塊主要用於創建子進程,並連接它們的輸入、輸出和錯誤管道,獲取它們的返回狀態。通俗地說就是通過這個模塊,你可以在Python的代碼里執行操作系統級別的命令,比如“ipconfig”、“du -sh”等等。subprocess模塊替代了一些老的模塊和函數,比如:
os.system
os.spawn*
subprocess過去版本中的call(),check_call()和check_output()已經被run()方法取代了。run()方法為3.5版本新增。大多數情況下,推薦使用run()方法調用子進程,執行操作系統命令。在更高級的使用場景,你還可以使用Popen接口。其實run()方法在底層調用的就是Popen接口。
二:subprocess的run方法
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, encoding=None, errors=None)
功能:執行args參數所表示的命令,等待命令結束,並返回一個CompletedProcess類型對象。
下面是run參數的作用:
args:表示要執行的命令。必須是一個字符串,字符串參數列表。
stdin、stdout和stderr:子進程的標准輸入、輸出和錯誤。其值可以是subprocess.PIPE、subprocess.DEVNULL、一個已經存在的文件描述符、已經打開的文件對象或者None。subprocess.PIPE表示為子進程創建新的管道。subprocess.DEVNULL表示使用os.devnull。默認使用的是None,表示什么都不做。另外,stderr可以合並到stdout里一起輸出。
timeout:設置命令超時時間。如果命令執行時間超時,子進程將被殺死,並彈出TimeoutExpired異常。
check:如果該參數設置為True,並且進程退出狀態碼不是0,則彈出CalledProcessError異常。
encoding:如果指定了該參數,則stdin、stdout和stderr可以接收字符串數據,並以該編碼方式編碼。否則只接收bytes類型的數據。
shell:如果該參數為True,將通過操作系統的shell執行指定的命令,如果執行命令時遇見權限不足的境況,可以將此參數設置為True
注意,run()方法返回的不是我們想要的執行結果或相關信息,而是一個CompletedProcess類型對象。
>>> r = subprocess.run("ping www.baidu.com") 正在 Ping www.a.shifen.com [220.181.38.150] 具有 32 字節的數據: 來自 220.181.38.150 的回復: 字節=32 時間=17ms TTL=52 來自 220.181.38.150 的回復: 字節=32 時間=17ms TTL=52 來自 220.181.38.150 的回復: 字節=32 時間=19ms TTL=52 來自 220.181.38.150 的回復: 字節=32 時間=18ms TTL=52 220.181.38.150 的 Ping 統計信息: 數據包: 已發送 = 4,已接收 = 4,丟失 = 0 (0% 丟失), 往返行程的估計時間(以毫秒為單位): 最短 = 17ms,最長 = 19ms,平均 = 17ms >>> type(r) <class 'subprocess.CompletedProcess'> >>> r CompletedProcess(args='ping www.baidu.com', returncode=0) >>>
可以看到,run方法的執行結果是一個CompletedProcess類型對象。
下面是CompletedProcess類型對象的一些屬性:
args 啟動進程的參數,通常是個列表或字符串。 returncode 進程結束狀態返回碼。0表示成功狀態。 stdout 獲取子進程的stdout。通常為bytes類型序列,None表示沒有捕獲值。如果你在調用run()方法時,設置了參數stderr=subprocess.STDOUT,則錯誤信息會和stdout一起輸出,此時stderr的值是None。 stderr() 獲取子進程的錯誤信息。通常為bytes類型序列,None表示沒有捕獲值。 check_returncode() 用於檢查返回碼。如果返回狀態碼不為零,彈出CalledProcessError異常。
獲取狀態碼:
r = subprocess.run("ping www.baidu.com") 正在 Ping www.a.shifen.com [220.181.38.150] 具有 32 字節的數據: 來自 220.181.38.150 的回復: 字節=32 時間=35ms TTL=52 來自 220.181.38.150 的回復: 字節=32 時間=29ms TTL=52 來自 220.181.38.150 的回復: 字節=32 時間=16ms TTL=52 來自 220.181.38.150 的回復: 字節=32 時間=18ms TTL=52 220.181.38.150 的 Ping 統計信息: 數據包: 已發送 = 4,已接收 = 4,丟失 = 0 (0% 丟失), 往返行程的估計時間(以毫秒為單位): 最短 = 16ms,最長 = 35ms,平均 = 24ms>>> r.returncode 0
獲取命令執行后的內容:
run()方法返回的是一個CompletedProcess類型對象,不能直接獲取我們通常想要的結果。要獲取命令執行的結果或者信息,在調用run()方法的時候,請指定stdout=subprocess.PIPE。
>>> ret = subprocess.run('dir', shell=True, stdout=subprocess.PIPE) >>> ret CompletedProcess(args='dir', returncode=0, stdout=b' \xc7\xfd\xb6\xaf\xc6\xf7 C \xd6\xd0\xb5\xc4\xbe\xed\xca\xc7 Windows 10\r\n \xbe\xed\xb5\xc4\xd0\xf2\xc1\xd0\xba\xc5\xca\xc7 02DE-BFF0\r\n\r\n C:\\Users\\lwy \xb5\xc4\xc4\xbf\xc2\xbc\r\n\r\n2019/12/31 10:29 <DIR> .\r\n2019/12/31 10:29 <DIR> ..\r\n2019/10/16 10:27 <DIR> .3T\r\n2019/09/23 20:31 <DIR> .anaconda\r\n2019/10/07 13:14 <DIR> .android\r\n2019/07/23 09:54 <DIR> .astropy\r\n2019/12/28 19:01 4,807 .bash_history\r\n2019/09/26 18:19 <DIR> .conda\r\n2019/09/26 18:19 151 .condarc\r\n2019/10/07 10:18 <DIR> .config\r\n2019/11/02 11:50 1,126 .dbshell\r\n2019/07/31 16:49 181 .gitconfig\r\n2019/07/22 20:31 <DIR> .ipython\r\n2019/09/23 16:15 <DIR> .keras\r\n2019/11/06 21:47 <DIR> .matplotlib\r\n2019/08/01 09:36 37 .minttyrc\r\n2019/10/06 20:53 <DIR> .mitmproxy\r\n2019/10/01 15:20 0 .mongorc.js\r\n2019/08/30 15:19 <DIR> .oracle_jre_usage\r\n2019/07/21 23:57 <DIR> .PyCharm2019.1\r\n2019/12/10 17:04 25 .python_history\r\n2019/07/31 16:04 <DIR> .rdm\r\n2019/07/31 16:38 35 .rediscli_history\r\n2019/07/22 20:31 <DIR> .spyder-py3\r\n2019/09/17 17:51 5,339 .viminfo\r\n2019/12/12 13:21 <DIR> 3D Objects\r\n2019/12/12 13:21 <DIR> Contacts\r\n2019/12/31 13:41 <DIR> Desktop\r\n2019/12/18 15:47 <DIR> Documents\r\n2019/12/30 16:34 <DIR> Downloads\r\n2019/12/12 13:21 <DIR> Favorites\r\n2019/10/15 16:36 <DIR> Funshion\r\n2019/12/12 13:21 <DIR> Links\r\n2019/12/12 13:21 <DIR> Music\r\n2019/07/21 23:58 <DIR> OneDrive\r\n2019/12/12 13:21 <DIR> Pictures\r\n2019/12/12 13:21 <DIR> Saved Games\r\n2019/12/12 13:21 <DIR> Searches\r\n2019/12/31 10:27 <DIR> test22\r\n2019/12/12 13:21 <DIR> Videos\r\n 9 \xb8\xf6\xce\xc4\xbc\xfe 11,701 \xd7\xd6\xbd\xda\r\n 31 \xb8\xf6\xc4\xbf\xc2\xbc 51,676,090,368 \xbf\xc9\xd3\xc3\xd7\xd6\xbd\xda\r\n') >>> type(ret) <class 'subprocess.CompletedProcess'> >>>
可以看到,這時候返回的內容就是命令的執行結果了,是一個CompletedProcess的類型,也可以通過指定編碼使返回對象是一個字符串類型。
>>> ret = subprocess.run('dir', shell=True, stdout=subprocess.PIPE).stdout.decode("gbk") >>> ret ' 驅動器 C 中的卷是 Windows 10\r\n 卷的序列號是 02DE-BFF0\r\n\r\n C:\\Users\\lwy 的目錄\r\n\r\n2019/12/31 10:29 <DIR> .\r\n2019/12/31 10:29 <DIR> ..\r\n2019/10/16 10:27 <DIR> .3T\r\n2019/09/23 20:31 <DIR> .anaconda\r\n2019/10/07 13:14 <DIR> .android\r\n2019/07/23 09:54 <DIR> .astropy\r\n2019/12/28 19:01 4,807 .bash_history\r\n2019/09/26 18:19 <DIR> .conda\r\n2019/09/26 18:19 151 .condarc\r\n2019/10/07 10:18 <DIR> .config\r\n2019/11/02 11:50 1,126 .dbshell\r\n2019/07/31 16:49 181 .gitconfig\r\n2019/07/22 20:31 <DIR> .ipython\r\n2019/09/23 16:15 <DIR> .keras\r\n2019/11/06 21:47 <DIR> .matplotlib\r\n2019/08/01 09:36 37 .minttyrc\r\n2019/10/06 20:53 <DIR> .mitmproxy\r\n2019/10/01 15:20 0 .mongorc.js\r\n2019/08/30 15:19 <DIR> .oracle_jre_usage\r\n2019/07/21 23:57 <DIR> .PyCharm2019.1\r\n2019/12/10 17:04 25 .python_history\r\n2019/07/31 16:04 <DIR> .rdm\r\n2019/07/31 16:38 35 .rediscli_history\r\n2019/07/22 20:31 <DIR> .spyder-py3\r\n2019/09/17 17:51 5,339 .viminfo\r\n2019/12/12 13:21 <DIR> 3D Objects\r\n2019/12/12 13:21 <DIR> Contacts\r\n2019/12/31 13:41 <DIR> Desktop\r\n2019/12/18 15:47 <DIR> Documents\r\n2019/12/30 16:34 <DIR> Downloads\r\n2019/12/12 13:21 <DIR> Favorites\r\n2019/10/15 16:36 <DIR> Funshion\r\n2019/12/12 13:21 <DIR> Links\r\n2019/12/12 13:21 <DIR> Music\r\n2019/07/21 23:58 <DIR> OneDrive\r\n2019/12/12 13:21 <DIR> Pictures\r\n2019/12/12 13:21 <DIR> Saved Games\r\n2019/12/12 13:21 <DIR> Searches\r\n2019/12/31 10:27 <DIR> test22\r\n2019/12/12 13:21 <DIR> Videos\r\n 9 個文件 11,701 字節\r\n 31 個目錄 51,681,050,624 可用字節\r\n' >>> type(ret) <class 'str'> >>>
三:subprocess的Popen方法
並不是所有的操作系統命令都像‘dir’或者‘ipconfig’那樣單純地返回執行結果,還有很多像‘python’這種交互式的命令,你要輸入點什么,然后它返回執行的結果。subprocess中的Popen方法,可以執行一些交互性的命令。run方法也可以進行一些輸入,不過很不方便,也不是以代碼的形式驅動的,想要了解的同學可以看下文末大佬的原文。
Popen的用法和參數與run()方法基本類同,但是它的返回值是一個Popen對象,而不是CompletedProcess對象。
>>> r = subprocess.Popen("dir", shell=True)>>> type(r) <class 'subprocess.Popen'> >>> r <subprocess.Popen object at 0x000001921134EC48> >>>
要‘python’命令功能,可以按下面的例子操作:
import subprocess s = subprocess.Popen("python", stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True) s.stdin.write(b"import os\n") s.stdin.write(b"print(os.environ)") s.stdin.close() out = s.stdout.read().decode("GBK") s.stdout.close() print(out)
另外,也可以把需要執行的后續命令卸載一個txt文件里,打開這個文件並賦值給stdin這個參數:
f = open("111.txt", "r+") s = subprocess.Popen("python",stdout=subprocess.PIPE, stdin=f, shell=True) out = s.stdout.read().decode("utf-8") s.stdout.close() print(out)
111.txt文件中的內容是:
1 import os 2 print(os.getcwd())
注意:每行代碼后面要加換行。
參考文章:http://www.liujiangblog.com/course/python/55
********************不積跬步無以至千里********************
