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模块大写吗_shell

 

subprocess模块大写吗_subprocess模块大写吗_02

 

标准写法



subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)



  

涉及到管道|的命令需要这样写



subprocess.run('df -h|grep disk1',shell=True) #shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析


  

subprocess模块大写吗_系统命令_03

    

subprocess模块大写吗_子进程_04

 

 

 

  (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


 

subprocess模块大写吗_shell_05

 

  (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分钟,可以继续往下走,干别的事情,每过一会,通过一个什么方法来检测一下命令是否执行完成就好了。

  

subprocess模块大写吗_python_06

   

subprocess模块大写吗_shell_07

 

Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法

(1)poll()



Check if child process has terminated. Returns returncode
# 检查子进程是否死亡


 

subprocess模块大写吗_子进程_08

 

(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 拿到所启动进程的进程号


 

subprocess模块大写吗_subprocess模块大写吗_09

 

 

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


  

subprocess模块大写吗_python_10