我們經常需要通過Python去執行一條系統指令或腳本,系統的shell指令是獨立于你的python程序之外的,每執行一條指令,就是發起一個新程序,通過python調用系統指令或腳本的子產品在python2有os.system
os.system() 結果輸出在終端上,會傳回執行指令的狀态碼,我們可以用變量來接收
>>> import os
>>> os.system('hostname')
mysql
0
>>>
傳回的0就是這個linux指令執行狀态,0就是代表指令傳回成功,指令執行不成功就是非0
這個指令執行狀态 相當于echo $?
我們看到把指令 指派給a a變量最後傳回是0,傳回是指令執行狀态,不是指令的結果
>>> a = os.system('df')
檔案系統 1K-塊 已用 可用 已用% 挂載點
/dev/sda5 5039616 2757016 2026600 58% /
tmpfs 135720 0 135720 0% /dev/shm
/dev/sda1 99150 27098 66932 29% /boot
/dev/sda8 481685144 1082644 456134276 1% /data
/dev/sda6 5039616 141080 4642536 3% /home
/dev/sda2 10079084 2660636 6906448 28% /usr
/dev/sda3 10079084 274856 9292228 3% /var
>>> a
0
這個通過os.system()是拿不到指令結果的,但我們可以通過os.popen() 可以拿到
os.popen() 他的原理是在記憶體打開一個臨時檔案把 指令結果存到這個檔案裡,把檔案内容讀出來 相當于檔案操作
>>> os.popen('df')
<os._wrap_close object at 0x7f36e2410518>
拿到結果了
>>> f = os.popen('df')
>>> f
<os._wrap_close object at 0x7f36e2373390>
>>> f.read()
'檔案系統\t 1K-塊 已用 可用 已用% 挂載點\n/dev/sda5 5039616 2757016 2026600 58% /\ntmpfs 135720 0 135720 0% /dev/shm\n/dev/sda1 99150 27098 66932 29% /boot\n/dev/sda8 481685144 1082644 456134276 1% /data\n/dev/sda6 5039616 141080 4642536 3% /home\n/dev/sda2 10079084 2660636 6906448 28% /usr\n/dev/sda3 10079084 274864 9292220 3% /var\n'
在read() 沒有了
>>> f.read()
''
除了os.system可以調用系統指令,,commands,popen2等也可以,比較亂,python3清理一些不規範東西,于是官方推出了subprocess,目的是提供統一的子產品來實作對系統指令或腳本的調用
python3沒有了commands子產品 , subprocess子產品就是為了替換commands子產品 os.system 這些子產品
subprocess子產品可以調用作業系統指令,python在linux可以執行shell指令,subprocess 每執行一條指令,
會開啟一個子程序(即shell)來執行指令,相當于每執行一條指令,打開一個獨立程式視窗,這個就是程序:
subprocess子產品
三種執行指令的方法
- subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推薦
- subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面實作的内容差不多,另一種寫法
- subprocess.Popen() #上面各種方法的底層封裝
subprocess.run run方法
傳回一個對象,執行一條指令傳回一個對象,通過對象拿到指令結果
加上清單意思,裡面輸入指令參數傳給subprocess ,subprocess會幫你拼接成完整的shell指令
>>> import subprocess
>>>
>>> subprocess.run(['df','-h'])
檔案系統 容量 已用 可用 已用%% 挂載點
/dev/sda5 4.9G 2.7G 2.0G 58% /
tmpfs 133M 0 133M 0% /dev/shm
/dev/sda1 97M 27M 66M 29% /boot
/dev/sda8 460G 1.1G 436G 1% /data
/dev/sda6 4.9G 138M 4.5G 3% /home
/dev/sda2 9.7G 2.6G 6.6G 28% /usr
/dev/sda3 9.7G 269M 8.9G 3% /var
CompletedProcess(args=['df', '-h'], returncode=0)
>>> a
CompletedProcess(args=['df', '-h'], returncode=0)
通過對象去拿結果
# 傳回指令傳回狀态
>>> a.returncode
0
# 傳回指令參數
>>> a.args
['df', '-h']
python怎麼去拿結果?
标準寫法
subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)
stderr是标準錯誤,輸出錯誤資訊。 stdout是标準輸出,輸出正确資訊。
PIPE是一個管道,管道相當于建立一個程序執行指令,指令結果通過管道傳回python的标準輸出,如果指令執行錯誤了,
管道傳回到标準錯誤
管道就是作業系統,借作業系統記憶體把管道拿出來
# 把執行指令結果存到管道裡
>>> a = subprocess.run(['df','-h'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
# 把結果讀出來,标準輸出
>>> a.stdout
b'Filesystem Size Used Avail Use% Mounted on\n/dev/sda5 4.9G 2.7G 2.0G 58% /\ntmpfs 133M 0 133M 0% /dev/shm\n/dev/sda1 97M 27M 66M 29% /boot\n/dev/sda8 460G 1.1G 436G 1% /data\n/dev/sda6 4.9G 138M 4.5G 3% /home\n/dev/sda2 9.7G 2.6G 6.6G 28% /usr\n/dev/sda3 9.7G 269M 8.9G 3% /var\n'
#沒有錯誤資訊,标準錯誤
>>> a.stderr
b''
check=True 如果指令傳回一個非0的執行狀态,給程式報錯
我們輸入一個沒有的參數
>>> a = subprocess.run(['df','-sssh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,)
>>> a.stdout
b''
# 出錯
>>> a.stderr
b"df: invalid option -- 's'\nTry `df --help' for more information.\n"
出錯,但整個程式沒有報錯
加上check=True 程式直接報錯了 傳回非0 指令執行狀态
>>> a = subprocess.run(['df','-sssh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/python3.6/lib/python3.6/subprocess.py", line 418, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['df', '-sssh']' returned non-zero exit status 1.
現在我們執行一些複雜的指令, 用linux指令的管道 + grep 過濾一些資訊出來
通過作業系統的管道,交給grep 過濾一些資訊
[root@mysql ~]# df -h|grep sda3
/dev/sda3 9.7G 269M 8.9G 3% /var
在subprocess.run()執行這條指令,
報錯了
>>> a = subprocess.run(['df','-h','|','sda3'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/python3.6/lib/python3.6/subprocess.py", line 418, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['df', '-h', '|', 'sda3']' returned non-zero exit status 1.
通過subprocess 執行一個 涉及到管道|的指令需要這樣寫
需要不寫清單了,然後加上 shell=True
#shell=True的意思是這條指令 不需要幫忙拼接參數 ,直接交給系統去執行shell指令
可以了
>>> a = subprocess.run('df -h|grep sda3',stderr=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)
>>> a.stderr
b''
>>> a.stdout
b'/dev/sda3 9.7G 269M 8.9G 3% /var\n'
>>>
subprocess.call() 方法 介紹
和run方法一樣的
call() 執行指令,傳回指令執行狀态 , 0 or 非0
#執行指令,傳回指令執行狀态 , 0 or 非0
>>> subprocess.call(['ls','-la'])
total 240
dr-xr-x---. 8 root root 4096 Feb 26 23:33 .
dr-xr-xr-x. 24 root root 4096 Feb 26 06:39 ..
-rw-------. 1 root root 1080 Oct 6 01:40 anaconda-ks.cfg
-rw-------. 1 root root 5449 Feb 26 23:33 .bash_history
-rw-r--r--. 1 root root 18 May 20 2009 .bash_logout
-rw-r--r--. 1 root root 176 May 20 2009 .bash_profile
-rw-r--r--. 1 root root 176 Sep 23 2004 .bashrc
-rw-r--r--. 1 root root 15624 Feb 24 06:39 blog.sql
drwx------. 3 root root 4096 Feb 26 07:37 .cache
-rw-r--r--. 1 root root 100 Sep 23 2004 .cshrc
-rwxr-xr-x. 1 root root 118 Feb 26 21:29 getMemory.py
-rw-r--r--. 1 root root 7730 Oct 6 01:40 install.log
-rw-r--r--. 1 root root 3384 Oct 6 01:38 install.log.syslog
-rw-------. 1 root root 43579 Feb 23 09:25 .mysql_history
-rw-r--r--. 1 root root 13811 Nov 22 09:32 oot
drwxr-xr-x. 2 root root 4096 Nov 15 02:09 .oracle_jre_usage
drwxr-----. 3 root root 4096 Oct 6 02:09 .pki
-rw-------. 1 root root 694 Feb 26 23:29 .python_history
drwxr-xr-x. 3 root root 4096 Nov 19 01:32 .subversion
-rw-r--r--. 1 root root 129 Dec 4 2004 .tcshrc
-rw-------. 1 root root 6578 Feb 26 23:33 .viminfo
0
>>> a = subprocess.call(['ls','-la'])
total 240
dr-xr-x---. 8 root root 4096 Feb 26 23:33 .
dr-xr-xr-x. 24 root root 4096 Feb 26 06:39 ..
-rw-------. 1 root root 1080 Oct 6 01:40 anaconda-ks.cfg-rw-------. 1 root root 5449 Feb 26 23:33 .bash_history
-rw-r--r--. 1 root root 18 May 20 2009 .bash_logout
-rw-r--r--. 1 root root 176 May 20 2009 .bash_profile
-rw-r--r--. 1 root root 176 Sep 23 2004 .bashrc
-rw-r--r--. 1 root root 15624 Feb 24 06:39 blog.sql
drwx------. 3 root root 4096 Feb 26 07:37 .cache-rw-r--r--. 1 root root 7730 Oct 6 01:40 install.log
-rw-r--r--. 1 root root 3384 Oct 6 01:38 install.log.syslog
-rw-------. 1 root root 43579 Feb 23 09:25 .mysql_history
-rw-r--r--. 1 root root 13811 Nov 22 09:32 oot
drwxr-xr-x. 2 root root 4096 Nov 15 02:09 .oracle_jre_usage
drwxr-----. 3 root root 4096 Oct 6 02:09 .pki
-rw-------. 1 root root 694 Feb 26 23:29 .python_history
drwxr-xr-x. 3 root root 4096 Nov 19 01:32 .subversion
-rw-r--r--. 1 root root 129 Dec 4 2004 .tcshrc
-rw-------. 1 root root 6578 Feb 26 23:33 .viminfo
>>> a
0
subprocess.check_call
用法和call方法一樣的,不同的是,執行指令,如果指令結果為0,就正常傳回,否則抛異常,
會抛出 CalledProcessError 異常,我們可以根據這個異常去決定需要做什麼
>>> subprocess.check_call(['df','-h'])
Filesystem Size Used Avail Use% Mounted on
/dev/sda5 4.9G 2.7G 2.0G 58% /
tmpfs 133M 0 133M 0% /dev/shm
/dev/sda1 97M 27M 66M 29% /boot
/dev/sda8 460G 1.1G 436G 1% /data
/dev/sda6 4.9G 138M 4.5G 3% /home
/dev/sda2 9.7G 2.6G 6.6G 28% /usr
/dev/sda3 9.7G 269M 8.9G 3% /var
0
>>> a = subprocess.check_call(['df','-h'])
Filesystem Size Used Avail Use% Mounted on
/dev/sda5 4.9G 2.7G 2.0G 58% /
tmpfs 133M 0 133M 0% /dev/shm
/dev/sda1 97M 27M 66M 29% /boot
/dev/sda8 460G 1.1G 436G 1% /data
/dev/sda6 4.9G 138M 4.5G 3% /home
/dev/sda2 9.7G 2.6G 6.6G 28% /usr
/dev/sda3 9.7G 269M 8.9G 3% /var
>>> a
0
>>> a = subprocess.check_call(['df','-hd'])
df: invalid option -- 'd'
Try `df --help' for more information.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/python3.6/lib/python3.6/subprocess.py", line 291, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['df', '-hd']' returned non-zero exit status 1.
subprocess.getstatusoutput() 方法
接收字元串格式指令,傳回元組形式,第1個元素是執行狀态,第2個是指令結果
>>> subprocess.getstatusoutput('df -h')
(0, 'Filesystem Size Used Avail Use% Mounted on\n/dev/sda5 4.9G 2.7G 2.0G 58% /\ntmpfs 133M 0 133M 0% /dev/shm\n/dev/sda1 97M 27M 66M 29% /boot\n/dev/sda8 460G 1.1G 436G 1% /data\n/dev/sda6 4.9G 138M 4.5G 3% /home\n/dev/sda2 9.7G 2.6G 6.6G 28% /usr\n/dev/sda3 9.7G 269M 8.9G 3% /var')
subprocess.getoutput() 方法
接收字元串格式指令,并傳回結果,隻傳回結果
>>> subprocess.getoutput('df -h')
'Filesystem Size Used Avail Use% Mounted on\n/dev/sda5 4.9G 2.7G 2.0G 58% /\ntmpfs 133M 0 133M 0% /dev/shm\n/dev/sda1 97M 27M 66M 29% /boot\n/dev/sda8 460G 1.1G 436G 1% /data\n/dev/sda6 4.9G 138M 4.5G 3% /home\n/dev/sda2 9.7G 2.6G 6.6G 28% /usr\n/dev/sda3 9.7G 269M 8.9G 3% /var'
subprocess.check_output() 方法
執行指令,并傳回結果,注意是傳回結果,不是列印,下例結果傳回給res
>>> subprocess.check_output(['df', '-h'])
b'Filesystem Size Used Avail Use% Mounted on\n/dev/sda5 4.9G 2.7G 2.0G 58% /\ntmpfs 133M 0 133M 0% /dev/shm\n/dev/sda1 97M 27M 66M 29% /boot\n/dev/sda8 460G 1.1G 436G 1% /data\n/dev/sda6 4.9G 138M 4.5G 3% /home\n/dev/sda2 9.7G 2.6G 6.6G 28% /usr\n/dev/sda3 9.7G 269M 8.9G 3% /var\n'
>>>
>>> res = subprocess.check_output(['df', '-h'])
>>> res
b'Filesystem Size Used Avail Use% Mounted on\n/dev/sda5 4.9G 2.7G 2.0G 58% /\ntmpfs 133M 0 133M 0% /dev/shm\n/dev/sda1 97M 27M 66M 29% /boot\n/dev/sda8 460G 1.1G 436G 1% /data\n/dev/sda6 4.9G 138M 4.5G 3% /home\n/dev/sda2 9.7G 2.6G 6.6G 28% /usr\n/dev/sda3 9.7G 269M 8.9G 3% /var\n'
Popen()方法
常用參數:
- args:shell指令,可以是字元串或者序列類型(如:list,元組)
- stdin, stdout, stderr:分别表示程式的标準輸入、輸出、标準錯誤
- preexec_fn:隻在Unix平台下有效,用于指定一個可執行對象(callable object),它将在子程序運作之前被調用,執行指令之前可以調一個python函數
- shell:同上shell=True
- cwd:用于設定子程序的目前目錄
- env:用于指定子程序的環境變量。如果env = None,子程序的環境變量将從父程序中繼承。 設定環境變量
#shell=True的意思是這條指令 不需要幫忙拼接參數 ,直接交給系統去執行shell指令
subprocess.Popen() :用于執行 shell 指令,結果傳回三個對象,分别是标準輸入,标準輸出,标準錯誤輸出
Popen調用後會傳回一個對象,可以通過這個對象拿到指令執行結果或狀态等,該對象有以下方法
poll()
檢查指令有沒有執行結束,執行結束傳回指令執行狀态
>>> a = subprocess.Popen('df -h',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
>>> a
<subprocess.Popen object at 0x7f54f190de10>
# poll() 傳回指令執行狀态
>>> a.poll()
0
下面這2條語句執行會有什麼差別?
a=subprocess.run('sleep 10',shell=True,stdout=subprocess.PIPE)
a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)
差別是Popen會在發起指令後立刻傳回,而不等指令執行結果。這樣的好處是什麼呢?
如果你調用的指令或腳本 需要執行10分鐘,你的主程式不需卡在這裡等10分鐘,可以繼續往下走,幹别的事情,每過一會,通過一個poll()方法來檢測一下指令是否執行完成就好了。