Python程式設計之子程序管理(subprocess)詳解

2022-10-13 15:05:11

引言

 

在寫程式時,我們無法避免需要執行外部程式,相較於功能比較簡單的os.system(),更加傾向於使用subprocess模組來執行外部程式

 

模組介紹

subprocess.run() 

使用subprocess.run()執行命令的時候,父程序會一直等待直到子程序結束後才會繼續執行父程序

 

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, 
          encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs) """ 引數介紹 1. args: cmd命令 2. stdin: 傳遞引數進來 3. input: 傳遞引數進來,使用input的時候不能使用stdin 4. stdout: 外部程式的輸出,可以指定通過管道(subprocess.PIPE) 5. stderr: 外部程式的報錯輸出, 可以指定通過管道(subprocess.PIPE)或者和stdout使用同一控制程式碼(stderr=subprocess.STDOUT) 6. capture_output: 同時獲取stdout和stderr 7. shell: 是否通過shell執行命令 8. cwd: 命令執行的工作目錄 9. timeout: 如果超時則終止子程序,該引數被傳遞給Popen.communicate() 10. check: 檢查returncode是否為0,如果不為0則引發subprocess.CalledProcessError錯誤, 可以通過try....except...捕獲 11. encoding: 編碼型別 12. errors: 13. text: 可用來代替universal_newlines 14. env: 設定環境變數 15. universal_newlines: 返回資料以文字字串輸出,否則為二進位制輸出 """

 

範例

import subprocess as sp

# 三種方式構造命令
sp.run('ls -l', shell=True)

sp.run(['ls', '-l'], shell=True)

sp.run(' '.join(['ls', '-l']), shell=True)

# 判斷是否正確執行命令
sp.run('ls -l', shell=True, check=True)

# 獲取命令的輸出
p = sp.run('ls -l', shell=True, check=True, stdout=sp.PIPE, stderr=sp.PIPE)

print(p.stdout.read())

# 使用stdin接受資料傳入
p1 = sp.run('ls -l', shell=True, check=True, stdout=sp.PIPE, stderr=sp.PIPE)

print(p1.stdout.read())

p2 = sp.run('grep lovefish', shell=True, check=True, stdin=p1.stdout, stdout=sp.PIPE, stderr=sp.PIPE)

print(p2.stdout.read())
例子

 

subprocess.Popen()

subprocess.Popen(args, bufsize=- 1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, 
          shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None,
          creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *,
          group=None, extra_groups=None, user=None, umask=- 1, encoding=None, errors=None, text=None, pipesize=- 1) """ 引數介紹 1. args:cmd命令,字串或者列表 2. bufsize:0:無緩衝; 1:行緩衝,只可以在universal_newlines=True時被使用;其他正值則為緩衝區的大小;負數則為使用系統預設緩衝 3. executable:一般不使用,用來表示shell程式 4. stdin:傳遞資料進來 5. stdout:命令的輸出,可以指定通過管道輸出(subprocess.PIPE) 6. stderr:命令的報錯輸出,可以通過管道(subprocess.PIPE)或者和stdout使用同一控制程式碼輸出(subprocess.STDOUT) 7. preexec_fns: 在exec之前執行 8. close_fds:如果為真,在unix下,則關閉除0,1,2之外的檔案。在windows下無法設定close_fds為真和重定向stderr和stdout 9. shell:是否通過shell執行命令 10. cwd:命令執行的工作目錄 11. env:設定環境變數 12. universal_newlines:讓返回資料以文字字串輸出 13. pipesize:設定sp.PIPE的大小 函數介紹 1. Popen.poll():檢查子程序是否結束 2. Popen.wait():等待直到子程序結束 3. Popen.communicate():內部資料互動,將資料傳送給stdin,返回stdout和stderr 4. Popen.send_signal():傳送訊號給子程序 5. Popen.terminate():終止子程序,unix下對應SIGTERM,windows下對應TerminateProcess() 6. Popen.kill():殺死子程序,unix下對應SIGKILL,windows下和terminate()一致 物件介紹 1. Popen.args:命令 2. Popen.stdout:命令的輸出 3. Popen.stderr:命令的報錯輸出 4. Popen.stdin:命令接受的資料 5. Popen.pid:子程序的ID 6. Popen.returncode:返回值 """

 

範例

import subprocess as sp

#  父程序不等待子程序
p = sp.Popen('ls -l', shell=True,  stdout=sp.PIPE, stderr=sp.PIPE)

# 父程序等待子程序結束之後再繼續執行
p = sp.Popen('ls -l', shell=True,  stdout=sp.PIPE, stderr=sp.PIPE)

p.wait()

# 使用內容管理器
with Popen(["ls -l"], stdout=PIPE) as proc: 
  print(proc.stdout.read())

 

注意

在使用管道(PIPE)輸出stdout或者stderr時,請注意輸出的資料量不能超過PIPE的上限,否則就會出現PIPE被阻塞,導致程式被阻塞無法繼續執行,可以通過使用Popen.communicate()把stdout和stderr的輸出存到記憶體中來緩解由於PIPE過小導致subprocess.Popen()無法繼續執行程式的問題

 Reference

subprocess 官方檔案