在 Python 中調(diào)用外部進(jìn)程是很常見的需求,以下為幾種常見的方法谴仙。
os.system
這個方法實際上調(diào)用的是系統(tǒng)的 C 函數(shù) system(), 命令以字符串的形式傳入篱瞎,在系統(tǒng)子 shell 中執(zhí)行,和在真實的 shell 執(zhí)行一致堰乔。
優(yōu)點:可以利用 shell 的管道和重定向等特性來實現(xiàn)比較復(fù)雜的命令秀撇。
import os
# 使用管道獲取所有java進(jìn)程并強(qiáng)制殺掉
os.system('ps -ef|grep java|cut -c 9-15|xargs kill -9')
# 獲取當(dāng)前目錄下所有png圖片文件名并重定向到png_list.txt中
os.system('ls *.png > png_list.txt')
缺點:外部進(jìn)程的輸出無法被 python 代碼捕獲,system 函數(shù)只返回一個 exit status舍肠,此外作為命令傳入的字符串必須是有效的命令搀继,否則會出現(xiàn)意想不到的結(jié)果。
os.popen
與上一種不同的是貌夕,os.popen 會開辟一個管道并將輸出通過一個 file-like 的對象返回給調(diào)用者律歼,類似 python 中文件操作方法 open 一樣。
import os
with os.popen('ls *.png', mode='r') as res:
result = res.read()
subprocess.Popen
這個是 python 自帶庫 subprocess 中的 Popen 類啡专,作為 os.popen 的替代险毁,擁有更多可選參數(shù)。
import subprocess
# 當(dāng) shell=True 時们童,POSIX 類系統(tǒng)默認(rèn)會調(diào)用 /bin/sh 來執(zhí)行傳入的命令
result = subprocess.Popen(['ls', '-l'], shell=True, stdout=subprocess.PIPE).stdout.read()
subprocess.call
這個函數(shù)擁有類似 Popen 類的可選參數(shù)畔况,但是會等待子進(jìn)程運(yùn)行完畢再返回一個返回碼。
import subprocess
return_code = subprocess.call(['ls', '-l'], shell=True)
subprocess.run
這個函數(shù)在 python3.5 及以后的版本可用慧库,有幾個非常有用的參數(shù)可選跷跪,該函數(shù)返回的是一個 CompletedProcess 對象,通過該對象可以獲取標(biāo)準(zhǔn)輸入和輸出齐板。
import subprocess
# 在shell中執(zhí)行并獲取標(biāo)準(zhǔn)輸出和錯誤
result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# 可以配合 shlex 庫分割指令字符串
# subprocess.run(shlex.split('ls -l))
print(result.stdout)
print(result.stderr)
# 對返回值進(jìn)行檢查, 如果子程序返回錯誤那么拋出一個CalledProcessError的異常
result = subprocess.run(['ls', '-l'],check=True)
# 給子程序加上超時限制, 如果超時, 向上拋出TimeoutExpired異常, 在項目中控制外部進(jìn)程用時是比較好的習(xí)慣
result = subprocess.run(['sleep', '5'], timeout=3)
fabric.operations
from fabric import operations
res = operations.local('ls -l', capture=True)
# 在遠(yuǎn)程服務(wù)器上執(zhí)行命令吵瞻,但需要提前配置好登陸信息
res = operations.run('cmd')
第三方模塊 sh
其通過動態(tài)解析 $PATH 來執(zhí)行二進(jìn)制命令,執(zhí)行并不是 Python 類或函數(shù)甘磨,而是用 python 包裝了系統(tǒng) PATH 中的可執(zhí)行命令橡羞,也就是說系統(tǒng)路徑中所有命令都是可以執(zhí)行的。
# 安裝
pip install sh
# 執(zhí)行各種系統(tǒng)命令
sh.ls('-l', '/data')
sh.ifconfig()
# 使用管道济舆,并獲取返回值
count = sh.wc(sh.ls('-l'), '-l')
# 甚至可以使用子命令
sh.git.status()
# 將命令放在后臺運(yùn)行
p = sh.find('-name', 'sh.py', _bg=True)
# 干其他事
p.wait()
總結(jié)
個人優(yōu)先推薦官方庫中的 subprocess 模塊中的 run 函數(shù)卿泽,簡單易用。