23-[模块]-subprocess模块
1.调用系统命令
我们经常需要通过Python去执行一条系统命令或脚本,系统的shell命令是独立于你的python进程之外的,每执行一条命令,就是发起一个新进程,通过python调用系统命令或脚本的模块在python2有os.system
>>> os.system('uname -a') Darwin Alexs-MacBook-Pro.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun 4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64 0
(1)os.system
>>> import os >>> os.system('df -h') Filesystem Size Used Avail Use% Mounted on C:/Program Files (x86)/cmder/vendor/git-for-windows 224G 86G 139G 39% / D: 101G 1001M 100G 1% /d E: 123G 129M 122G 1% /e F: 122G 1.3G 121G 1% /f G: 123G 3.6G 119G 3% /g 0 >>> os.system('uname') MSYS_NT-6.1-WOW 0 >>>
(2)os.popen
>>> f = os.popen('df -h') >>> f <os._wrap_close object at 0x03501750> >>> f.read() 'Filesystem Size Used Avail Use% Mounted on\n C:/Program Files (x86)/cmder/vendor/git-for-windows 224G 86G 139G 39% /\n D: 101G 1001M 100G 1% /d\n E: 123G 129M 122G 1% /e\n F: 122G 1.3G 121G 1% /f\n G: 123G 3.6G 119G 3% /g\n' >>> >>> f.close() >>>
(3)commands #python2
>>> import commands >>> commands.getstatusoutput('df -h') (0, '\xe6\x96\x87\xe4\xbb\xb6\xe7\xb3\xbb\xe7\xbb\x9f \xe5\xae\xb9\xe9\x87\x8f \xe5\xb7\xb2\xe7\x94\xa8 \xe5\x8f\xaf\xe7\x94\xa8 \xe5\xb7\xb2\xe7\x94\xa8% \xe6\x8c\x82\xe8\xbd\xbd\xe7\x82\xb9\nudev 973M 0 973M 0% /dev\ntmpfs 199M 6.4M 192M 4% /run\n/dev/sda1 21G 11G 8.8G 56% /\ntmpfs 992M 200K 992M 1% /dev/shm\ntmpfs 5.0M 4.0K 5.0M 1% /run/lock\ntmpfs 992M 0 992M 0% /sys/fs/cgroup\ntmpfs 199M 56K 199M 1% /run/user/1000') >>>
这条命令的实现原理是什么呢?(视频中讲,解释进程间通信的问题...)
除了os.system可以调用系统命令,,commands,popen2等也可以,比较乱,于是官方推出了subprocess,目地是提供统一的模块来实现对系统命令或脚本的调用
2、subprocess模块
三种执行命令的方法
-
subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推荐
-
subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面实现的内容差不多,另一种写法
-
subprocess.Popen() #上面各种方法的底层封装
(1)run()方法
功能:执行args参数所表示的命令,等待命令结束,并返回一个CompletedProcess类型对象。
Run command with arguments and return a CompletedProcess instance.The returned instance will have attributes args, returncode, stdout and stderr. By default, stdout and stderr are not captured, and those attributes will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them. If check is True and the exit code was non-zero, it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute, and output & stderr attributes if those streams were captured. If timeout is given, and the process takes too long, a TimeoutExpired exception will be raised. The other arguments are the same as for the Popen constructor.
>>> import subprocess >>> subprocess.run(['df','-h'])
标准写法
subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)
涉及到管道|的命令需要这样写
subprocess.run('df -h|grep disk1',shell=True) #shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析
(2) call()方法
#执行命令,返回命令执行状态 , 0 or 非0 >>> retcode = subprocess.call(["ls", "-l"]) #执行命令,如果命令结果为0,就正常返回,否则抛异常 >>> subprocess.check_call(["ls", "-l"]) 0 #接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 >>> subprocess.getstatusoutput('ls /bin/ls') (0, '/bin/ls') #接收字符串格式命令,并返回结果 >>> subprocess.getoutput('ls /bin/ls') '/bin/ls' #执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res >>> res=subprocess.check_output(['ls','-l']) >>> res
(3)Popen()方法
常用参数:
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- shell:同上
- cwd:用于设置子进程的当前目录
- env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
下面这2条语句执行会有什么区别?
a=subprocess.run('sleep 10',shell=True,stdout=subprocess.PIPE) a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)
区别是Popen会在发起命令后立刻返回,而不等命令执行结果。这样的好处是什么呢?
如果你调用的命令或脚本 需要执行10分钟,你的主程序不需卡在这里等10分钟,可以继续往下走,干别的事情,每过一会,通过一个什么方法来检测一下命令是否执行完成就好了。
Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法
(1)poll()
Check if child process has terminated. Returns returncode
# 检查子进程是否死亡
(2)wait()
Wait for child process to terminate. Returns returncode attribute.
(3)terminate()、kill()
terminate()终止所启动的进程Terminate the process with SIGTERM kill() 杀死所启动的进程 Kill the process with SIGKILL
# 向文件写入数字 In [35]: a=subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >> /tmp/sleep.log;done',shell=True,stdout=subprocess.PIPE) # 文件内容 python@ubuntu:~$ tail -f /tmp/sleep.log 1 2 3 4 5 6 In [37]: a.pid #查看进程号 Out[37]: 7572 In [38]: a.kill() #停止写入
In [39]: a=subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >> /tmp/sleep.log;done',shell=True,stdout=subprocess.PIPE) In [40]: a.pid Out[40]: 7629 In [41]: import signal In [42]: import os In [45]: os.kill(7629,signal.SIGTERM)
(4)发送系统信号
send_signal(signal.xxx)发送系统信号
pid 拿到所启动进程的进程号
(5)communicate()
communicate()与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束 >>> a = subprocess.Popen('python3 guess_age.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True) >>> a.communicate(b'22') (b'your guess:try bigger\n', b'')