subprocess 模塊容許咱們啓動一個新進程,並鏈接到它們的輸入/輸出/錯誤管道,從而獲取返回值。java
這個模塊用來建立和管理子進程。它提供了高層次的接口,用來替換os.system*()、 os.spawn*()、 os.popen*()、os,popen2.*()和commands.*等模塊和函數。python
subprocess提供了一個名爲Popen的類啓動和設置子進程的參數,因爲這個類比較複雜, subprocess還提供了若干便利的函數,這些函數都是對Popen類的封裝。linux
pip3 install ipython
subprocess.ca11(args, *, stdin=None, stdout=None, stderr=None, she11=False) #運行由args參數提供的命令,等待命令執行結束並返回返回碼。args參數由字符串形式提供且有多個命令參數時,須要提供shell=True參數
[root@python ~]# ipython #啓動ipython Python 3.8.1 (default, Mar 9 2020, 12:35:12) Type 'copyright', 'credits' or 'license' for more information IPython 7.13.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import subprocess #調用函數 In [2]: subprocess.call(['ls','-l']) drwxr-xr-x. 2 root root 6 10月 31 23:04 公共 drwxr-xr-x. 2 root root 6 10月 31 23:04 模板 drwxr-xr-x. 2 root root 6 10月 31 23:04 視頻 drwxr-xr-x. 2 root root 4096 10月 31 22:40 圖片 drwxr-xr-x. 2 root root 6 10月 31 23:04 文檔 drwxr-xr-x. 2 root root 6 10月 31 23:04 下載 drwxr-xr-x. 2 root root 6 10月 31 23:04 音樂 drwxr-xr-x. 2 root root 6 10月 31 15:27 桌面 Out[2]: 0 In [3]: subprocess.call('exit 1',shell=True) Out[3]: 1
check_call函數的做用與call函數相似,區別在於異常狀況下返回的形式不一樣。mongodb
對於call函數,工程師經過捕獲call命令的返回值判斷命令是否執行成功,若是成功則返回0,不然的話返回非0,對於check_call函數,若是執行成功,返回0,若是執行失敗,拋出subrocess.CalledProcessError異常。以下所示:shell
In [5]: subprocess.check_call(['ls','-l']) drwxr-xr-x. 2 root root 6 10月 31 23:04 公共 drwxr-xr-x. 2 root root 6 10月 31 23:04 模板 drwxr-xr-x. 2 root root 6 10月 31 23:04 視頻 drwxr-xr-x. 2 root root 4096 10月 31 22:40 圖片 drwxr-xr-x. 2 root root 6 10月 31 23:04 文檔 drwxr-xr-x. 2 root root 6 10月 31 23:04 下載 drwxr-xr-x. 2 root root 6 10月 31 23:04 音樂 drwxr-xr-x. 2 root root 6 10月 31 15:27 桌面 Out[5]: 0 In [6]: subprocess.check_call('exit 1',shell=True) ------------------------------------------------------------- CalledProcessError Traceback (most recent call last) <ipython-input-6-5e148d3ce640> in <module> ----> 1 subprocess.check_call('exit 1',shell=True) /usr/local/python381/lib/python3.8/subprocess.py in check_call(*popenargs, **kwargs) 362 if cmd is None: 363 cmd = popenargs[0] --> 364 raise CalledProcessError(retcode, cmd) 365 return 0 366 CalledProcessError: Command 'exit 1' returned non-zero exit status 1.
Python3中的subprocess.check_output函數能夠執行一條sh命令,並返回命令的輸出內容,用法以下:數據庫
In [10]: output = subprocess.check_output(['df','-h']) In [11]: print(output.decode()) 文件系統 容量 已用 可用 已用% 掛載點 /dev/mapper/cl-root 17G 5.2G 12G 31% / devtmpfs 473M 0 473M 0% /dev tmpfs 489M 92K 489M 1% /dev/shm tmpfs 489M 7.1M 482M 2% /run tmpfs 489M 0 489M 0% /sys/fs/cgroup /dev/sda1 1014M 173M 842M 18% /boot tmpfs 98M 16K 98M 1% /run/user/42 tmpfs 98M 0 98M 0% /run/user/0 In [12]: lines = output.decode().split('\n') In [13]: lines Out[13]: ['文件系統 容量 已用 可用 已用% 掛載點', '/dev/mapper/cl-root 17G 5.2G 12G 31% /', 'devtmpfs 473M 0 473M 0% /dev', 'tmpfs 489M 92K 489M 1% /dev/shm', 'tmpfs 489M 7.1M 482M 2% /run', 'tmpfs 489M 0 489M 0% /sys/fs/cgroup', '/dev/sda1 1014M 173M 842M 18% /boot', 'tmpfs 98M 16K 98M 1% /run/user/42', 'tmpfs 98M 0 98M 0% /run/user/0', ''] In [14]: for line in lines[1:-1]: ...: if line: ...: print(line.split()[-2]) ...: #截取掛載點數據 31% 0% 1% 2% 0% 18% 1% 0%
在子進程執行命令,以字符串形式返回執行結果的輸出。若是子進程退出碼不是0,拋出subprocess.CalledProcessError異常,異常的output字段包含錯誤輸出:apache
In [19]: try: ...: output = subprocess.check_output(['df','-h']).decode() #正確的 ...: except subprocess.CalledProcessError as e: ...: output = e.output ...: code = e.returncode //正確的沒有任何輸出 In [23]: try: ...: output = subprocess.check_output(['wsd','-h'], stderr=subprocess.STDOUT) ...: .decode() #錯誤的 ...: except subprocess.CalledProcessError as e: ...: output = e.output ...: code = e.returncode ...: //前面的錯誤代碼省略 FileNotFoundError: [Errno 2] No such file or directory: 'wsd'
實際上,咱們上面的三個函數都是基於Popen()的封裝(wrapper)。這些封裝的目的在於讓咱們容易使用子進程。當咱們想要更個性化咱們的需求的時候,就要轉向Popen類,該類生成的對象用來表明子進程。api
subprocess
模塊中基本的進程建立和管理由Popen
類來處理subprocess.popen
是用來替代os.popen
的subprocess
的核心,子進程的建立和管理都靠它處理。構造函數:緩存
class 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=False, startupinfo=None, creationflags=0,restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
>>> import subprocess >>> p = subprocess.Popen('ls -l', shell=True) >>> total 164 -rw-r--r-- 1 root root 133 Jul 4 16:25 admin-openrc.sh -rw-r--r-- 1 root root 268 Jul 10 15:55 admin-openrc-v3.sh ... >>> p.returncode >>> p.wait() 0 >>> p.returncode 0
這裏也可使用 p = subprocess.Popen(['ls', '-cl'])
來建立子進程。bash
p.pid
:子進程的PID。
p.returncode
:該屬性表示子進程的返回狀態,returncode可能有多重狀況:
p.stdin, p.stdout, p.stderr
:子進程對應的一些初始文件,若是調用Popen()的時候對應的參數是subprocess.PIPE,則這裏對應的屬性是一個包裹了這個管道的 file 對象。
子進程的PID存儲在child.pid
import time import subprocess def cmd(command): subp = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8") subp.wait(2) if subp.poll() == 0: print(subp.communicate()[1]) else: print("失敗") cmd("java -version") cmd("exit 1")
java version "1.8.0_31" Java(TM) SE Runtime Environment (build 1.8.0_31-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode) 失敗
(沿用child子進程) 子進程的標準輸入,標準輸出和標準錯誤也能夠經過以下屬性表示:
import subprocess child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE) out = child2.communicate() print(out)
(b' 2 11 60\n', None)
subprocess.PIPE實際上爲文本流提供一個緩存區。child1的stdout將文本輸出到緩存區,隨後child2的stdin從該PIPE中將文本讀取走。child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
要注意的是,communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成。
import subprocess child = subprocess.Popen(["cat"], stdin=subprocess.PIPE) child.communicate("vamei".encode())
咱們啓動子進程以後,cat會等待輸入,直到咱們用communicate()輸入"vamei"。
經過使用subprocess包,咱們能夠運行外部程序。這極大的拓展了Python的功能。若是你已經瞭解了操做系統的某些應用,你能夠從Python中直接調用該應用(而不是徹底依賴Python),並將應用的結果輸出給Python,並讓Python繼續處理。shell的功能(好比利用文本流鏈接各個應用),就能夠在Python中實現。
PyCharm記得鏈接linux
Python自動化運維 --> 基於shell命令進行封裝
編寫自動化腳本 --> 用Python語法封裝shell命令的執行過程
python執行shell命令 --> python外部命令
python函數執行shell命令
os.system(cmd):執行cmd指令
subprocess模塊
subprocess.call(['ls','-l']) subprocess.call('ll' , shell=True) 運行成功: 返回0 運行失敗: 返回非0
subprocess. check_call (['ls', '-l']) subprocess. check_call ('ll', shell=True) 運行成功: 返回0 運行失敗: 返回CalledProcessError
subprocess. check_ output(['cat', 'apache.log'], stderr= subprocess.STDOUT) 運行成功:返回命令的輸出結果 運行失敗:自定義錯誤輸出stderr
# coding=utf-8 import subprocess import os import shutil import tarfile # 執行外部命令的函數 def execute_cmd(cmd): '''執行shell命令''' p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: return p.returncode, stderr return p.returncode, stdout # 解壓 def unpackage_mongo(package, package_dir): # 獲取MongoDB壓縮包的主文件名,也就是解壓後的目錄名稱 # mongodb-linux-x86_64-rhe170-4.2.3 unpackage_dir = os.path.splitext(package)[0] if os.path.exists(unpackage_dir): shutil.rmtree(unpackage_dir) if os.path.exists(package_dir): shutil.rmtree(package_dir) # 解壓 try: t = tarfile.open(package, 'r:gz') t.extractall('.') print('tar is ok.') except Exception as e: print(e) # 重命名 shutil.move(unpackage_dir, 'mongo') # 建立mongodata def create_datadir(data_dir): if os.path.exists(data_dir): shutil.rmtree(data_dir) os.mkdir(data_dir) # 拼接啓動MongoDB def format_mongod_commamd(package_dir, data_dir, logfile): # mongo/bin/mongod mongod = os.path.join(package_dir, 'bin', 'mongod') # mongo/bin/mongod --fork --logpath mongodata/mongod.log --dbpath mongodata mongod_format = """{0} --fork --dbpath {1} --logpath {2}""" return mongod_format.format(mongod, data_dir, logfile) # 啓動MongoDB def start_mongod(cmd): returncode, out = execute_cmd(cmd) if returncode != 0: raise SystemExit('execute {0} error:{1}'.format(cmd, out)) else: print('execute {0} successfuly.'.format(cmd)) #入口函數 def main(): package = 'mongodb-linux-x86_64-rhel70-4.2.3.tgz' cur_dir = os.path.abspath('.') package_dir = os.path.join(cur_dir, 'mongo') data_dir = os.path.join(cur_dir, 'mongodata') logfile = os.path.join(data_dir, 'mongod.log') # 判斷MongoDB壓縮包是否存在 if not os.path.exists(package): raise SystemExit('{0} not found.'.format(package)) # 解壓 unpackage_mongo(package, package_dir) create_datadir(data_dir) # 啓動mongodb start_mongod(format_mongod_commamd(package_dir, data_dir, logfile)) # 配置環境變量 os.system('echo "export PATH=./mongo/bin:$PATH" > ~/.bash_profile') os.system('source ~/.bash_profile') os.system('./mongo/bin/mongo') main()
記得進入PyCharm與linux鏈接的目錄(目前是/opt)
[root@python opt]# python auto_install_mongodb.py #執行提早編寫好的腳本 tar is ok. execute /opt/mongo/bin/mongod --fork --dbpath /opt/mongodata --logpath /opt/mongodata/mongod.log successfuly. [root@python opt]# netstat -anpt | grep mongo #查看mongo是否啓動 tcp 0 0 127.0.0.1:27017 0.0.0.0:* LISTEN 4616mongod [root@python opt]# ls #查看是否生成mongo目錄 01find_cmd.py bb.bmp mongodb-linux-x86_64-rhel70-4.2.3.tgz aaa.jpg cc.png rh adc.txt mongo subprocess_demo auto_install_mongodb.py mongodata [root@python opt]# cd mongo [root@python mongo]# cd bin/ [root@python bin]# ./mongo #進入mongo MongoDB shell version v4.2.3 connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("c302ff50-7e27-40b7-8046-8441af8cb965") } MongoDB server version: 4.2.3 > show databases; #查看數據庫 admin 0.000GB config 0.000GB local 0.000GB