Python使用subprocess模塊輕松調(diào)用外部程序與命令
01 subprocess模塊簡(jiǎn)介
subprocess是Python的標(biāo)準(zhǔn)庫(kù),它的設(shè)計(jì)目標(biāo)是替代舊有的os.system、os.popen等模塊,提供更統(tǒng)一、更強(qiáng)大的子進(jìn)程管理功能。使用subprocess可以啟動(dòng)外部程序、獲取程序執(zhí)行結(jié)果、管理進(jìn)程的輸入輸出和錯(cuò)誤信息,在自動(dòng)化腳本、系統(tǒng)管理、程序集成等場(chǎng)景中發(fā)揮重要作用。
02 核心函數(shù)介紹與案例
1. subprocess.run():執(zhí)行命令并等待完成
subprocess.run()是subprocess模塊中最常用的函數(shù),用于執(zhí)行指定的命令,并等待命令執(zhí)行完畢。它接受args參數(shù)(命令及其參數(shù)),以及input等可選參數(shù),返回一個(gè)CompletedProcess對(duì)象,包含命令執(zhí)行結(jié)果信息。
參數(shù)介紹:
1)popenargs:指定要執(zhí)行的命令及參數(shù)
popenargs是subprocess.run()中最核心的參數(shù),必須傳入,它用于指定要執(zhí)行的外部命令及其參數(shù)。args通常以列表形式傳遞,列表的第一個(gè)元素是命令本身,后續(xù)元素為命令的參數(shù)。
案例:執(zhí)行簡(jiǎn)單命令
import subprocess
# 執(zhí)行l(wèi)s命令,獲取當(dāng)前目錄下的文件列表
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
if result.returncode == 0:
print("命令執(zhí)行成功,輸出如下:")
print(result.stdout)
else:
print(f"命令執(zhí)行失敗,錯(cuò)誤信息:{result.stderr}")說(shuō)明:代碼中使用subprocess.run()執(zhí)行l(wèi)s -l命令,capture_output=True表示捕獲命令的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出,text=True則以文本形式返回輸出結(jié)果。通過(guò)檢查returncode判斷命令是否執(zhí)行成功。
2)stdin、stdout、stderr:控制輸入輸出和錯(cuò)誤流
- stdin:指定子進(jìn)程的標(biāo)準(zhǔn)輸入,可以是文件對(duì)象、字節(jié)串或字符串(當(dāng)text=True時(shí))。如果不指定,子進(jìn)程的標(biāo)準(zhǔn)輸入將繼承父進(jìn)程的標(biāo)準(zhǔn)輸入。
- stdout:指定子進(jìn)程的標(biāo)準(zhǔn)輸出,常見取值為subprocess.PIPE(用于捕獲輸出)、文件對(duì)象或None(默認(rèn)繼承父進(jìn)程的標(biāo)準(zhǔn)輸出)。
- stderr:指定子進(jìn)程的標(biāo)準(zhǔn)錯(cuò)誤輸出,取值和用法與stdout類似。
案例:捕獲命令的輸出和錯(cuò)誤信息
import subprocess
# 執(zhí)行一個(gè)錯(cuò)誤的命令,捕獲輸出和錯(cuò)誤
result = subprocess.run(['ls', 'nonexistent_file'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print("標(biāo)準(zhǔn)輸出:", result.stdout)
print("標(biāo)準(zhǔn)錯(cuò)誤:", result.stderr)
#輸出為:
標(biāo)準(zhǔn)輸出:
標(biāo)準(zhǔn)錯(cuò)誤: ls: nonexistent_file: No such file or directory3)capture_output:快速捕獲輸出和錯(cuò)誤
capture_output是一個(gè)布爾值參數(shù),當(dāng)設(shè)置為True時(shí),相當(dāng)于同時(shí)將stdout和stderr設(shè)置為subprocess.PIPE,并自動(dòng)捕獲子進(jìn)程的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。
案例:使用capture_output簡(jiǎn)化代碼
import subprocess
# 執(zhí)行一個(gè)錯(cuò)誤的命令,捕獲輸出和錯(cuò)誤
result = subprocess.run(['ls', 'nonexistent_file'],capture_output=True, text=True)
print("標(biāo)準(zhǔn)輸出:", result.stdout)
print("標(biāo)準(zhǔn)錯(cuò)誤:", result.stderr)4)shell:使用系統(tǒng)shell執(zhí)行命令
shell是一個(gè)布爾值參數(shù),默認(rèn)為False。當(dāng)shell=True時(shí),命令會(huì)通過(guò)系統(tǒng)的shell(如bash)執(zhí)行,此時(shí)執(zhí)行命令可以是字符串形式,如下“ls nonexistent_file”。
import subprocess
# 執(zhí)行一個(gè)錯(cuò)誤的命令,捕獲輸出和錯(cuò)誤
result = subprocess.run('ls nonexistent_file',shell=True,capture_output=True, text=True)
print("標(biāo)準(zhǔn)輸出:", result.stdout)
print("標(biāo)準(zhǔn)錯(cuò)誤:", result.stderr)但這種方式可能會(huì)引入安全問(wèn)題,例如:
import subprocess
user_input = "; rm -rf /"# 惡意輸入示例
# 危險(xiǎn)操作!可能會(huì)刪除系統(tǒng)根目錄
subprocess.run(f'echo {user_input}', shell=True)因此,除非必要,否則不建議使用shell=True,若確實(shí)需要,可以考慮使用shlex.quote對(duì)輸入進(jìn)行轉(zhuǎn)義。
5)cwd:指定子進(jìn)程的工作目錄
cwd參數(shù)用于指定子進(jìn)程執(zhí)行時(shí)的工作目錄。如果不指定,子進(jìn)程將在父進(jìn)程的當(dāng)前工作目錄下執(zhí)行。
案例:在指定目錄下執(zhí)行命令
import subprocess # 在/tmp目錄下執(zhí)行l(wèi)s命令 result = subprocess.run(['ls'], cwd='/tmp', capture_output=True, text=True) print(result.stdout)
6)timeout:設(shè)置命令執(zhí)行的超時(shí)時(shí)間
timeout參數(shù)用于設(shè)置子進(jìn)程執(zhí)行的最長(zhǎng)時(shí)間(單位為秒)。如果子進(jìn)程在指定時(shí)間內(nèi)未執(zhí)行完畢,將引發(fā)subprocess.TimeoutExpired異常。
案例:設(shè)置命令執(zhí)行超時(shí)
import subprocess
try:
# 執(zhí)行ping命令,設(shè)置超時(shí)時(shí)間為5秒
result = subprocess.run(['ping', 'www.baidu.com'], timeout=5, capture_output=True, text=True)
print(result.stdout)
except subprocess.TimeoutExpired:
print("命令執(zhí)行超時(shí)!")7)check:檢查命令執(zhí)行狀態(tài)
check是一個(gè)布爾值參數(shù),默認(rèn)為False。當(dāng)check=True時(shí),如果子進(jìn)程的返回碼不為0,將引發(fā)
subprocess.CalledProcessError異常。
案例:檢查命令是否執(zhí)行成功
import subprocess
try:
# 執(zhí)行一個(gè)可能失敗的命令,開啟check檢查
result = subprocess.run(['ls','nonexistent_file'], check=True, capture_output=True, text=True)
except subprocess.CalledProcessError:
print("命令執(zhí)行失?。?)2. subprocess.Popen():創(chuàng)建并管理子進(jìn)程
subprocess.Popen()函數(shù)用于創(chuàng)建一個(gè)新的子進(jìn)程,并返回一個(gè)Popen對(duì)象,通過(guò)該對(duì)象可以控制子進(jìn)程的執(zhí)行、獲取輸出、與子進(jìn)程進(jìn)行交互。
subprocess.Popen()是subprocess模塊的底層接口,與run()的主要區(qū)別在于:
- 阻塞 vs 非阻塞:run()是阻塞式的(等待命令執(zhí)行完畢),而Popen()是非阻塞式的(立即返回進(jìn)程對(duì)象)。
- 靈活性:Popen()提供更底層的控制,適合需要實(shí)時(shí)交互或復(fù)雜進(jìn)程管理的場(chǎng)景。
參數(shù)介紹:
1)args:命令及參數(shù)
與run()相同,指定要執(zhí)行的命令。推薦使用列表形式,避免安全風(fēng)險(xiǎn)。
import subprocess
# 正確寫法:列表形式
p = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)
# 危險(xiǎn)寫法(shell=True):
# p = subprocess.Popen('ls -l', shell=True)2)stdin/stdout/stderr:控制輸入輸出
與run()類似,但更靈活,支持實(shí)時(shí)處理流數(shù)據(jù)。
常見取值:
- subprocess.PIPE:創(chuàng)建管道,用于捕獲輸出或提供輸入。
- 文件對(duì)象:將輸出 / 輸入重定向到文件。
- None(默認(rèn)):繼承父進(jìn)程。
案例:實(shí)時(shí)獲取命令輸出
import subprocess
# 執(zhí)行命令并實(shí)時(shí)獲取輸出
p = subprocess.Popen(['ping', 'www.baidu.com'], stdout=subprocess.PIPE, text=True)
# 逐行讀取輸出
for line in iter(p.stdout.readline, ''):
print(line.strip())
p.wait() # 等待進(jìn)程結(jié)束3)shell:通過(guò)shell執(zhí)行命令(慎用)
與run()的shell參數(shù)功能相同,但風(fēng)險(xiǎn)更高。僅在必要時(shí)使用,并確保輸入經(jīng)過(guò)嚴(yán)格過(guò)濾。
# 安全風(fēng)險(xiǎn)示例(切勿在生產(chǎn)環(huán)境使用)
user_input = "example.txt; rm -rf /"
subprocess.Popen(f'cat {user_input}', shell=True)4)cwd:指定工作目錄
與run()的cwd參數(shù)功能相同。
import subprocess # 在/tmp目錄下執(zhí)行命令 p = subprocess.Popen(['ls'], cwd='/tmp', stdout=subprocess.PIPE) print(p.stdout.read().decode())
5)env:自定義環(huán)境變量
覆蓋或添加子進(jìn)程的環(huán)境變量。(有些命令執(zhí)行時(shí)需要獲取環(huán)境變量信息,否則執(zhí)行shell命令可能會(huì)報(bào)錯(cuò))
import subprocess
import os
# 自定義環(huán)境變量
my_env = {**os.environ, 'MY_VAR': 'custom_value'}
p = subprocess.Popen(['printenv', 'MY_VAR'], env=my_env, stdout=subprocess.PIPE)
print(p.stdout.read().decode()) # 輸出: custom_value6)bufsize:控制緩沖區(qū)大小
- -1(默認(rèn)):使用系統(tǒng)默認(rèn)緩沖區(qū)大小。
- 0:無(wú)緩沖(僅適用于stdin、stdout、stderr為管道時(shí))。
- 1:行緩沖(僅在text=True時(shí)有效)。
- >1:指定緩沖區(qū)大小(字節(jié))。
# 無(wú)緩沖模式,實(shí)時(shí)獲取輸出 p = subprocess.Popen(['ls'], stdout=subprocess.PIPE, bufsize=0)
7)start_new_session(Windows/Linux)
在新會(huì)話中啟動(dòng)進(jìn)程。
# 在Windows或Linux上創(chuàng)建新會(huì)話 p = subprocess.Popen(['python', 'script.py'], start_new_session=True)
一些應(yīng)用場(chǎng)景
1). 實(shí)時(shí)監(jiān)控命令輸出
import subprocess
# 實(shí)時(shí)監(jiān)控日志文件
p = subprocess.Popen(['tail', '-f', '/var/log/syslog'], stdout=subprocess.PIPE, text=True)
try:
for line in iter(p.stdout.readline, ''):
print(f"日志: {line.strip()}")
except KeyboardInterrupt:
p.terminate() # 按Ctrl+C終止監(jiān)控2). 并行執(zhí)行多個(gè)命令
import subprocess
import time
# 并行執(zhí)行多個(gè)命令
commands = [
['sleep', '2'],
['ls', '-l'],
['echo', 'Hello']
]
processes = [subprocess.Popen(cmd) for cmd in commands]
# 等待所有進(jìn)程結(jié)束
for p in processes:
p.wait()
print("所有命令執(zhí)行完畢")3). 復(fù)雜交互場(chǎng)景(如FTP客戶端)
import subprocess
# 啟動(dòng)交互式FTP客戶端
p = subprocess.Popen(['ftp', 'ftp.example.com'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,text=True)
# 發(fā)送登錄命令
p.stdin.write('user\n')
p.stdin.write('password\n')
p.stdin.flush()
# 讀取響應(yīng)
output = p.stdout.read(1024)Popen對(duì)象的其他方法
方法 | 描述 |
p.wait(timeout) | 等待進(jìn)程結(jié)束并返回返回碼,可設(shè)置超時(shí)(超時(shí)拋出TimeoutExpired)。 |
p.communicate(input=None, timeout=None) | 與進(jìn)程交互(發(fā)送輸入、獲取輸出),等待進(jìn)程結(jié)束。 |
p.send_signal(signal) | 向進(jìn)程發(fā)送信號(hào)(如signal.SIGTERM)。 |
p.terminate() | 終止進(jìn)程(發(fā)送 SIGTERM 信號(hào))。 |
p.kill() | 強(qiáng)制殺死進(jìn)程(發(fā)送 SIGKILL 信號(hào))。 |
p.poll() | 檢查進(jìn)程是否結(jié)束,返回返回碼(未結(jié)束返回None)。 |
3. subprocess.check_output():執(zhí)行命令并返回輸出
subprocess.check_output()函數(shù)執(zhí)行指定命令,若命令執(zhí)行成功,返回命令的標(biāo)準(zhǔn)輸出;若執(zhí)行失?。ǚ祷卮a非 0),則拋出CalledProcessError異常。
案例:獲取系統(tǒng)磁盤空間信息
import subprocess
try:
result = subprocess.check_output(['df', '-h'], text=True)
print("磁盤空間信息:")
print(result)
except subprocess.CalledProcessError as e:
print(f"命令執(zhí)行失敗,錯(cuò)誤碼:{e.returncode},錯(cuò)誤信息:{e.output}")說(shuō)明:代碼執(zhí)行df -h命令獲取磁盤空間信息,若命令執(zhí)行失敗,捕獲異常并輸出錯(cuò)誤信息。
subprocess模塊賦予了Python與外部世界交互的強(qiáng)大能力,通過(guò)掌握這些核心函數(shù),我們可以在 Python開發(fā)中實(shí)現(xiàn)更豐富的功能,無(wú)論是自動(dòng)化運(yùn)維、數(shù)據(jù)采集,還是程序集成,subprocess都能成為你的得力助手。
以上就是Python使用subprocess模塊輕松調(diào)用外部程序與命令的詳細(xì)內(nèi)容,更多關(guān)于Python subprocess調(diào)用外部程序與命令的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實(shí)現(xiàn)自動(dòng)分析Windows事件日志
Windows事件日志是排障的第一現(xiàn)場(chǎng),本文教你用 Python 的 win32evtlog 模塊和 PowerShell 橋接方案,自動(dòng)化收集過(guò)濾和分析事件日志,感興趣的小伙伴可以了解下2026-05-05
Python的10道簡(jiǎn)單測(cè)試題(含答案)
這篇文章主要介紹了Python的10道簡(jiǎn)單測(cè)試題(含答案),學(xué)習(xí)了一段時(shí)間python的小伙伴來(lái)做幾道測(cè)試題檢驗(yàn)一下自己的學(xué)習(xí)成果吧2023-04-04
python存儲(chǔ)16bit和32bit圖像的實(shí)例
今天小編就為大家分享一篇python存儲(chǔ)16bit和32bit圖像的實(shí)例,具有的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
利用Python對(duì)文件夾下圖片數(shù)據(jù)進(jìn)行批量改名的代碼實(shí)例
今天小編就為大家分享一篇關(guān)于利用Python對(duì)文件夾下圖片數(shù)據(jù)進(jìn)行批量改名的代碼實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02
Python腳本實(shí)現(xiàn)掃描指定目錄下.py文件并提取import語(yǔ)句
這篇文章主要為大家詳細(xì)介紹了如何使用Python腳本實(shí)現(xiàn)掃描指定目錄下.py文件并提取import語(yǔ)句,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2026-03-03

