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()
'文件系统	         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    274864   9292220   3% /var
'

在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
/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.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'
Try `df --help' for more information.
"

出错,但整个程序没有报错

加上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
'
>>> 


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
/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')


subprocess.getoutput() 方法
接收字符串格式命令,并返回结果,只返回结果

>>> subprocess.getoutput('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'



subprocess.check_output() 方法
执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> subprocess.check_output(['df', '-h'])
b'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
'
>>> 
>>> res = subprocess.check_output(['df', '-h'])
>>> res
b'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
'

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()方法来检测一下命令是否执行完成就好了。

 

 
原文地址:https://www.cnblogs.com/mingerlcm/p/10444289.html