天天看點

python subprocess子產品

我們經常需要通過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()方法來檢測一下指令是否執行完成就好了。