TOUVE 发表于 2018-8-7 09:49:58

python的subprocess模块

  1 os与commands模块
  ​
  2 subprocess模块
  ​
  3 subprocess.Popen类
  我们几乎可以在任何操作系统上通过命令行指令与操作系统进行交互,比如Linux平台下的shell。
  ​
  ​那么我们如何通过Python来完成这些命令行指令的执行呢?另外,我们应该知道的是命令行指令的
  ​
  ​执行通常有两个我们比较关注的结果:
  1 命令执行的状态码--表示命令执行是否成功
  ​
  2 命令执行的输出结果--命令执行成功后的输出
  早期的Python版本中,我们主要是通过os.system()、os.popen().read()等函数来执行命令行指令的,另外还有一个很少使用的commands模块。
  ​
  ​但是从Python 2.4开始官方文档中建议使用的是subprocess模块,所以os模块和commands模块的相关函数在这里只提供一个简单的使用示例,我们重要要介绍的是subprocess模块。
  一、os与commands模块
  ​
  Python中提供了以下几个函数来帮助我们完成命令行指令的执行:
  函数名 描述
  os.system(command) 返回命令执行状态码,而将命令执行结果输出到屏幕
  os.popen(command).read() 可以获取命令执行结果,但是无法获取命令执行状态码
  commands.getstatusoutput(command) 返回一个元组(命令执行状态码, 命令执行结果)
  os.popen(command)函数得到的是一个文件对象,因此除了read()方法外还支持write()等方法,具体要根据command来定;
  commands模块只存在于Python 2.7中,且不支持windows平台,因此commands模块很少被使用。另外,commands模块实际上也是通过对os.popen()的封装来完成的。
  import os
  retcode = os.system('dir')
  ​
  import os
  ret = os.popen('dir').read()
  print(ret)
  需要注意的是commands模块不支持windows平台,因此该实例是在Linux平台下执行的
  import os
  os.system('ls')
  import commands
  retcode, ret = commands.getstatusoutput('ls -l')
  retcode
  0
  print(ret)
  通过查看commands模块提供的属性可知,它也提供了单独获取命令执行状态码和执行结果的函数,如下所示:
  dir(commands)
  ​
  ['all', 'builtins', 'doc', 'file', 'name', 'package', 'getoutput', 'getstatus', 'getstatusoutput', 'mk2arg', 'mkarg']
  subprocess – 创建附加进程 ,subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码。
  subprocess模块提供了一种一致的方法来创建和处理附加进程,与标准库中的其它模块相比,提供了一个更高级的接口。用于替换如下模块:
  os.system() , os.spawnv() , os和popen2模块中的popen()函数,以及 commands().

[*]subprocess模块中的常用函数
  函数 描述
  subprocess.run() Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
  ​
  subprocess.call() 执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。
  ​
  subprocess.check_call() Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(..., check=True)。
  ​
  subprocess.check_output() Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
  ​
  subprocess.getoutput(cmd) 接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
  ​
  subprocess.getstatusoutput(cmd) 执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。
  ​
  说明:
  在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用
  ​
  ​subproccess模块的功能;
  ​
  在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;
  ​
  subprocess.run()、subprocess.call()、subprocess.check_call()和subprocess.check_output()都是通过对subprocess.Popen的封装来实现的高级函数,因此如果我们需要更复杂功能时,可以通过subprocess.Popen来完成。
  ​
  subprocess.getoutput()和subprocess.getstatusoutput()函数是来自Python 2.x的commands模块的两个遗留函数。它们隐式的调用系统shell,并且不保证其他函数所具有的安全性和异常处理的一致性。另外,它们从Python 3.3.4开始才支持Windows平台。
  函数参数列表:
  subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, universal_newlines=False)
  subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
  subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
  subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
  subprocess.getstatusoutput(cmd)
  subprocess.getoutput(cmd)
  ​
  参数说明:
  args: 要执行的shell命令,默认应该是一个字符串序列,如['df', '-Th']或('df', '-Th'),也可以是一个字符串,如'df -Th',但是此时需要把shell参数的值置为True。
  ​
  shell: 如果shell为True,那么指定的命令将通过shell执行。如果我们需要访问某些shell的特性,如管道、文件名通配符、环境变量扩展功能,这将是非常有用的。当然,python本身也提供了许多类似shell的特性的实现,如glob、fnmatch、os.walk()、os.path.expandvars()、os.expanduser()和shutil等。
  ​
  check: 如果check参数的值是True,且执行命令的进程以非0状态码退出,则会抛出一个CalledProcessError的异常,且该异常对象会包含 参数、退出状态码、以及stdout和stderr(如果它们有被捕获的话)。
  ​
  stdout, stderr:
  run()函数默认不会捕获命令执行结果的正常输出和错误输出,如果我们向获取这些内容需要传递subprocess.PIPE,然后可以通过返回的CompletedProcess类实例的stdout和stderr属性或捕获相应的内容;
  ​
  call()和check_call()函数返回的是命令执行的状态码,而不是CompletedProcess类实例,所以对于它们而言,stdout和stderr不适合赋值为subprocess.PIPE;
  ​
  check_output()函数默认就会返回命令执行结果,所以不用设置stdout的值,如果我们希望在结果中捕获错误信息,可以执行stderr=subprocess.STDOUT。
  ​
  input: 该参数是传递给Popen.communicate(),通常该参数的值必须是一个字节序列,如果universal_newlines=True,则其值应该是一个字符串。
  ​
  universal_newlines: 该参数影响的是输入与输出的数据格式,比如它的值默认为False,此时stdout和stderr的输出是字节序列;当该参数的值设置为True时,stdout和stderr的输出是字符串。
  ​

[*]subprocess.CompletedProcess类介绍
  需要说明的是,subprocess.run()函数是Python3.5中新增的一个高级函数,其返回值是一个subprocess.CompletedPorcess类的实例,因此,subprocess.completedPorcess类也是Python 3.5中才存在的。它表示的是一个已结束进程的状态信息,
  ​
  ​它所包含的属性如下:
  args: 用于加载该进程的参数,这可能是一个列表或一个字符串
  ​
  returncode: 子进程的退出状态码。通常情况下,退出状态码为0则表示进程成功运行了;一个负值-N表示这个子进程被信号N终止了
  ​
  stdout: 从子进程捕获的stdout。这通常是一个字节序列,如果run()函数被调用时指定
  ​
  ​universal_newlines=True,则该属性值是一个字符串。如果run()函数被调用时指定
  ​
  ​stderr=subprocess.STDOUT,那么stdout和stderr将会被整合到这一个属性中,且stderr将会为None
  stderr: 从子进程捕获的stderr。它的值与stdout一样,是一个字节序列或一个字符串。如果stderr灭有被捕获的话,它的值就为None
  ​
  check_returncode(): 如果returncode是一个非0值,则该方法会抛出一个CalledProcessError异常。
  subprocess.run()
  subprocess.run(["ls", "-l"])# doesn't capture output
  CompletedProcess(args=['ls', '-l'], returncode=0)
  subprocess.run("exit 1", shell=True, check=True)
  Traceback (most recent call last):
  ...
  subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
  subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
  ​
  CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
  stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
  ​
  subprocess.call()
  subprocess.call(['ls','-l'])
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  0
  subprocess.call('ls -l', shell=True)
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  0
  subprocess.call(['ls','-l'], stdout=subprocess.DEVNULL)
  0
  subprocess.call(['ls','-l', '/test'])
  ​
  ls: 无法访问/test: 没有那个文件或目录
  ​
  2
  suprocess.check_call()
  subprocess.check_call(['ls','-l'])
  ​
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  0
  subprocess.check_call('ls -l', shell=True)
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  0
  subprocess.check_call('ls -l /test', shell=True)
  ls: 无法访问/test: 没有那个文件或目录
  Traceback (most recent call last):
  File &quot;<stdin>&quot;, line 1, in <module>
  File &quot;/usr/lib/python3.4/subprocess.py&quot;, line 557, in check_call
  raise CalledProcessError(retcode, cmd)
  subprocess.CalledProcessError: Command 'ls -l /test' returned non-zero exit status 2
  sbuprocess.check_output()
  ret = subprocess.check_output(['ls','-l'])
  print(ret)
  b' \xe5\x85\xac\xe5\x85\xb1\xe7\x9a\x84\ndrwxr-xr-x2 wader wader   4096 12\xe6\x9c\x8872015 \xe6\xa8\xa1\xe6\x9d\xbf\ndrwxr-xr-x2 wader wader   4096 12\xe6\x9c\x8872015 \xe8\xa7\x86\xe9\xa2\x91\ndrwxr-xr-x2 wader wader   4096 12\xe6\x9c\x8872015 \xe5\x9b\xbe\xe7\x89\x87\ndrwxr-xr-x2 wader wader   4096 12\xe6\x9c\x8872015 \xe6\x96\x87\xe6\xa1\xa3\ndrwxr-xr-x2 wader wader   40964\xe6\x9c\x88 132016 \xe4\xb8\x8b\xe8\xbd\xbd\ndrwxr-xr-x2 wader wader   4096 12\xe6\x9c\x8872015 \xe9\x9f\xb3\xe4\xb9\x90\ndrwxr-xr-x7 wader wader   40965\xe6\x9c\x88 262016 \xe6\xa1\x8c\xe9\x9d\xa2\n'
  ret = subprocess.check_output(['ls','-l'], universal_newlines=True)
  print(ret)
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  ​
  subprocess.getoutput()与subprocess.getstatusoutput()
  ret = subprocess.getoutput('ls -l')
  print(ret)
  ​
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  ​
  retcode, output = subprocess.getstatusoutput('ls -l')
  print(retcode)
  0
  print(output)
  ​
  ​
  总用量 160
  drwxr-xr-x2 wader wader   4096 12月72015 公共的
  drwxr-xr-x2 wader wader   4096 12月72015 模板
  drwxr-xr-x2 wader wader   4096 12月72015 视频
  drwxr-xr-x2 wader wader   4096 12月72015 图片
  drwxr-xr-x2 wader wader   4096 12月72015 文档
  drwxr-xr-x2 wader wader   40964月 132016 下载
  drwxr-xr-x2 wader wader   4096 12月72015 音乐
  drwxr-xr-x7 wader wader   40965月 262016 桌面
  ​
  ​
  retcode, output = subprocess.getstatusoutput('ls -l /test')
  print(retcode)
  2
  print(output)
  ls: 无法访问/test: 没有那个文件或目录

[*]运行外部命令  subprocess.call(command) 方法
  ​
  subprocess的call方法可以用于执行一个外部命令,但该方法不能返回执行的结果,只能返回执行的状态码: 成功(0) 或 错误(非0)
  subprocess.call():执行命令,并返回执行状态,其中shell参数为False时,命令需要通过列表的方式传入,当shell为True时,可直接传入命令
  call()方法中的command可以是一个列表,也可以是一个字符串,作为字符串时需要用原生的shell来执行:
  import subprocess
  #执行 df -hl 命令
  #方法1:
  subprocess.call(['ls','-l'])
  ​
  total 8
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
  0
  #方法2:
  subprocess.call(&quot;ls -l&quot;,shell=True)
  total 8
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
  0
  如上实例所示,虽然我们能看到执行的结果,但实际获取的值只是状态码
  output = subprocess.call(&quot;ls -l&quot;,shell=True)
  total 8
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
  print(output)
  0
  a = subprocess.call(['df','-hT'],shell=False)
  ​

  Filesystem    Type   >  /dev/sda2   ext4   94G   64G   26G72% /
  tmpfs      tmpfs    2.8G   02.8G   0% /dev/shm
  /dev/sda1   ext4    976M   56M853M   7% /boot
  a = subprocess.call('df -hT',shell=True)

  Filesystem    Type   >  /dev/sda2   ext4   94G   64G   26G72% /
  tmpfs      tmpfs    2.8G   02.8G   0% /dev/shm
  /dev/sda1   ext4    976M   56M853M   7% /boot
  print a
  0
  subprocess.check_call():用法与subprocess.call()类似,区别是,当返回值不为0时,直接抛出异常
  subprocess.check_call() 方法我们说过call执行返回一个状态码,我们可以通过check_call()函数来检测命令的执行结果,如果不成功将返回 subprocess.CalledProcessError 异常
  try:
  subprocess.check_call(&quot;ls -t&quot;, shell=True)
  except subprocess.CalledProcessError as err:
  print(&quot;Command Error&quot;)
  /bin/sh: lt: command not found
  Command Error
  a = subprocess.check_call('df -hT',shell=True)

  Filesystem    Type   >  /dev/sda2   ext4   94G   64G   26G72% /
  tmpfs      tmpfs    2.8G   02.8G   0% /dev/shm
  /dev/sda1   ext4    976M   56M853M   7% /boot
  print a
  0
  a = subprocess.check_call('dfdsf',shell=True)
  /bin/sh: dfdsf: command not found
  Traceback (most recent call last):
  File &quot;<stdin>&quot;, line 1, in <module>
  File &quot;/usr/lib64/python2.6/subprocess.py&quot;, line 502, in check_call
  raise CalledProcessError(retcode, cmd)
  subprocess.CalledProcessError: Command 'dfdsf' returned non-zero exit status 127
  subprocess.check_output():用法与上面两个方法类似,区别是,如果当返回值为0时,直接返回输出结果,如果返回值不为0,直接抛出异常。需要说明的是,该方法在python3.x中才有。
  call()方法启动的进程,其标准输入输出会绑定到父进程的输入和输出。调用程序无法获取命令的输出结果。但可以通过check_output()方法来捕获输出。
  output=subprocess.check_output(&quot;ls -l&quot;,shell=True)
  output
  b'total 8\ndrwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem\ndrwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp\n'
  print(output.decode('utf-8'))
  total 8
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
  以下例子将chek_output()方法执行命令异常时的错误捕获,而避免输出到控制台.
  try:
  output = subprocess.check_output(&quot;lT -l&quot;, shell=True, stderr=subprocess.STDOUT)
  except subprocess.CalledProcessError as err:
  print(&quot;Command Error&quot;, err)
执行结果
  Command Error Command 'lT -l' returned non-zero exit status 127
  直接处理管道
  subprocess.Popen()方法
  函数call(), check_call() 和 check_output() 都是Popen类的包装器。直接使用Popen会对如何运行命令以及如何处理其输入输出有更多控制。如通过为stdin, stdout和stderr传递不同的参数。
  subprocess.Popen():
  在一些复杂场景中,我们需要将一个进程的执行输出作为另一个进程的输入。在另一些场景中,我们需要先进入到某个输入环境,然后再执行一系列的指令等。这个时候我们就需要使用到suprocess的Popen()方法。该方法有以下参数:
  args:shell命令,可以是字符串,或者序列类型,如list,tuple。
  bufsize:缓冲区大小,可不用关心
  stdin,stdout,stderr:分别表示程序的标准输入,标准输出及标准错误
  shell:与上面方法中用法相同
  cwd:用于设置子进程的当前目录
  env:用于指定子进程的环境变量。如果env=None,则默认从父进程继承环境变量
  universal_newlines:不同系统的的换行符不同,当该参数设定为true时,则表示使用\n作为换行符
  示例1,在/root下创建一个suprocesstest的目录:
  a = subprocess.Popen('mkdir subprocesstest',shell=True,cwd='/root')
  示例2,使用python执行几个命令:
  import subprocess
  obj = subprocess.Popen([&quot;python&quot;], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  obj.stdin.write('print 1 \n')
  obj.stdin.write('print 2 \n')
  obj.stdin.write('print 3 \n')
  obj.stdin.write('print 4 \n')
  obj.stdin.close()
  cmd_out = obj.stdout.read()
  ​
  obj.stdout.close()
  ​
  cmd_error = obj.stderr.read()
  ​
  obj.stderr.close()
  print cmd_out
  print cmd_error
  也可以使用如下方法:
  import subprocess
  obj = subprocess.Popen([&quot;python&quot;], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  ​
  obj.stdin.write('print 1 \n')
  obj.stdin.write('print 2 \n')
  obj.stdin.write('print 3 \n')
  obj.stdin.write('print 4 \n')
  out_error_list = obj.communicate()
  print out_error_list
  示例3,将一个子进程的输出,作为另一个子进程的输入:
  import subprocess
  ​
  child1 = subprocess.Popen([&quot;cat&quot;,&quot;/etc/passwd&quot;], stdout=subprocess.PIPE)
  ​
  child2 = subprocess.Popen([&quot;grep&quot;,&quot;0:0&quot;],stdin=child1.stdout, stdout=subprocess.PIPE)
  ​
  out = child2.communicate()
  其他方法:
  import subprocess
  child = subprocess.Popen('sleep 60',shell=True,stdout=subprocess.PIPE)
  child.poll()    #检查子进程状态
  child.kill()   #终止子进程
  ​
  child.send_signal()    #向子进程发送信号
  child.terminate()   #终止子进程
  与进程的单向通信
  通过Popen()方法调用命令后执行的结果,可以设置stdout值为PIPE,再调用communicate()获取结果 返回结果为tuple. 在python3中结果为byte类型,要得到str类型需要decode转换一下
直接执行命令输出到屏幕
  ​
  subprocess.Popen(&quot;ls -l&quot;,shell=True)
  ​
  <subprocess.Popen object at 0x7febd4175198>
  total 12
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  -rw-rw-r-- 1 ws ws    8 Feb 25 10:38 test
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
不输出到屏幕,输出到变量
  proc = subprocess.Popen(['echo','&quot;Stdout&quot;'],stdout=subprocess.PIPE)
  ​
communicate返回标准输出或标准出错信息
  stdout_value = proc.communicate()
  ​
  stdout_value
  (b'&quot;Stdout&quot;\n', None)
  proc = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE)
  stdout_value = proc.communicate()
  ​
  stdout_value
  (b'total 8\ndrwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem\ndrwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp\n', None)
  print((stdout_value).decode('utf-8'))
  ​
  total 8
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
  #将结果输出到文件
  file_handle = open(&quot;/home/ws/t.log&quot;,'w+')
  ​
  subprocess.Popen(&quot;ls -l&quot;,shell=True,stdout=file_handle)
  ​
  ​
  t.log:
  drwxrwxr-x 4 ws ws 4096 Nov 25 13:50 MonitorSystem
  -rw-rw-r-- 1 ws ws    8 Feb 25 10:38 test
  -rw-rw-r-- 1 ws ws    0 Feb 25 11:24 t.log
  drwxrwxr-x 2 ws ws 4096 Feb 19 10:09 tmp
  proc = subprocess.Popen('cat', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
  ​
  msg = 'Hello world'.encode('utf-8')
  ​
写入到输入管道
  ​
  proc.stdin.write(msg)
  ​
  11
  stdout_value = proc.communicate()
  stdout_value
  (b'Hello world', None)
在需要进行相互交互的输入输出过程也可以使用shtin来实现
以下实现打开python3的终端,执行一个print命令
  ​
  proc = subprocess.Popen(['python3'],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE,)
  ​
  proc.stdin.write('print(&quot;helloworld&quot;)'.encode('utf-8'))
  out_value,err_value=proc.communicate()
  ​
  print(out_value)
  print(out_value)
  b'helloworld\n'
  print(err_value)
  b''
  Popen.communicate()方法用于和子进程交互:发送数据到stdin,并从stdout和stderr读数据,直到收到EOF。等待子进程结束。
  捕获错误输出
  proc = subprocess.Popen(['python3'],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE,)
  ​
  proc.stdin.write('print &quot;helloworld&quot;'.encode('utf-8'))
  ​
  18
  out_value,err_value=proc.communicate()
  out_value
  b''
  print(err_value.decode('utf-8'))
  File &quot;<stdin>&quot;, line 1
  print &quot;helloworld&quot;
  ^
  SyntaxError: Missing parentheses in call to 'print'
  Popen其它方法
  Popen.pid 查看子进程ID
  Popen.returncode 获取子进程状态码,0表示子进程结束,None未结束
  在使用Popen调用系统命令式,建议使用communicate与stdin进行交互并获取输出(stdout),这样能保证子进程正常退出而避免出现僵尸进程。看下面例子
  proc = subprocess.Popen('ls -l', shell=True, stdout=subprocess.PIPE)
当前子进程ID
  proc.pid
  28906
返回状态为None,进程未结束
  print(proc.returncode)
  None
通过communicate提交后
  out_value = proc.communicate()
  proc.pid
  28906
返回状态为0,子进程自动结束
  print(proc.returncode)
  0
  1.subprocess模块,
  res = os.system('dir') 打印到屏幕,res为0或非0
  os.popen('dir') 返回一个内存对象,相当于文件流
  ​
  a = os.popen('dir').read() a中就存的是执行结果输出了
  Python2.7 commands模块 commands.getstatusoutput('dir')返回元祖,第一个元素为状态0为成功,第二个为结果windows上不好用,只是Linux好用
  subprocess模块,替换os.system等
  subprocess.run(['df','-h']) 当参数传,Python解析,如果有管道符就不行了
  subprocess.run('df -h | grep sda1', shell=True) shell=True是指不需要Python解析,直接把字符串给shell
  Python3.5才出现subprocess.run
  终端输入的命令分为两种:
  输入即可得到输出,如:ifconfig
  输入进行某环境,依赖再输入,如:Python
  常用subprocess
  没有管道
  retcode = subprocess.call(['ls','-l']) 成功返回0,不成功返回非0
  subprocess.check_call(['ls','-l']) 执行成功返回0,执行错误抛异常
  subprocess.getoutput('ls /bin/ls')接收字符串格式命令,只返回结果
  ​
  res = subprocess.check_output(['ls','-l'])执行成功返回执行结果,不成功出错
  ​
  subprocess.getstatsoutput('ls /bin/ls') 返回元祖(1,'/bin/ls'),第一个状态,第二个结果
  上面的方法,底层都是封装subprocess.popen
  例子
  res = subprocess.popen('ifconfig | grep 192',shell=True)
  res
  <subprocess.popen object at ox7f2131a>
  res.stdout.read()读不出来
  ​
  要读出来要先输出到标准输出里,先存到管道PIPE 再给stdout python和shell是两个进程不能独立通信,必须通过操作系统提供的管道
  ​
  用管道可以把结果存到stdin stdout stderr
  ​
  subprocess.popen('ifconfig | grep 192',shell=True,stdout=subprocess.PIPE)
  ​
  res.stdout.read()就可以读出来了
  ​
  subprocess.popen('ifconfig | gr1111ep 192',shell=True,stdout=subprocess.PIPE)
  ​
  出错会直接打印错误。想不打印错误可以stderr保存stderr=subprocess.PIPE
poll() check if child process has terminated. returns returncode
res=subprocess.popen(&quot;sleep 10;echo 'hello'&quot;, shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  执行的时候没反应,不知道是卡主了还是执行完了
  每次调subprocess执行Linux命令,都相当于启动了一个新的shell,启动新的进程,执行一次命令等结果
  如果该命令要花半小时,不知道是卡主了还是执行完了,可以res.poll()返回none表示还没有执行完,返回0表示执行完了
  res.wait()等待结束然后返回0
  terminate()杀掉该进程,res.terminate()
  wait() wait for child process to terminate. returns returncode attribute
  communicate()等待任务结束 没什么用,用Python当参数,输Python进入环境
  stdin 标准输入
  stdout 标准输出
  stderr 标准错误

  pid the process>  -----可用参数
  args: shell命令,可以是字符串或者序列类型
  bufsize:指定缓冲,0无缓冲,1 行缓冲,其他 缓冲区大小 负值 系统缓冲
  stdin,stdout,stderr:标准输入,输出,错误句柄
  preexec_fn:只在Unix平台下有效,用于指定一个可执行对象,它将在子进程运行之前被调用
  close_sfs:在Windows平台下,如果close_sfs被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道
  所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误
  shell:同上
  cod:用于设置子进程的当前目录
  env:用于指定子进程的环境变量。如果env=None,子进程的环境变量将从父进程中继承
  universal_newlines:不同系统的换行符不同,True->同意使用\n
  startupinfo与createionflags只在Windows下有效
  将被传递给底层的createprocess()函数,用于设置子进程的一些属性,
  如:主窗口的外观,进程的优先级等
  subprocess实现sudo自动输入密码
  ​
  例如Python里面执行sudo apt-get install vim (Linux里面要输入密码)
  ​
  linux中应该echo '123' | sudo -S iptables -L
  ​
  python直接 subprocess.popen(&quot;echo '123' | sudo -S iptables -L&quot;,shell=True)
  subprocess.Popen使用实例
  实例1:
  import subprocess
  p = subprocess.Popen('df -Th', stdout=subprocess.PIPE, shell=True)
  ​
  print(p.stdout.read())
  ​

  Filesystem   Type   >  /dev/vda1      ext4       40G   12G   26G31% /
  devtmpfs       devtmpfs3.9G   03.9G   0% /dev
  tmpfs          tmpfs   3.9G   03.9G   0% /dev/shm
  tmpfs          tmpfs   3.9G386M3.5G10% /run
  tmpfs          tmpfs   3.9G   03.9G   0% /sys/fs/cgroup
  tmpfs          tmpfs   783M   0783M   0% /run/user/0
  tmpfs          tmpfs   783M   0783M   0% /run/user/1000
  实例2:
  obj = subprocess.Popen([&quot;python&quot;], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  ​
  obj.stdin.write('print(1) \n')
  obj.stdin.write('print(2) \n')
  obj.stdin.write('print(3) \n')
  out,err = obj.communicate()
  print(out)
  1
  2
  3
  print(err)
  实例3:
  obj = subprocess.Popen([&quot;python&quot;], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  ​
  out,err = obj.communicate(input='print(1) \n')
  ​
  print(out)
  1
  print(err)
  实例4:
  实现类似df -Th | grep data命令的功能,实际上就是实现shell中管道的共功能。
  p1 = subprocess.Popen(['df', '-Th'], stdout=subprocess.PIPE)
  p2 = subprocess.Popen(['grep', 'data'], stdin=p1.stdout, stdout=subprocess.PIPE)
  out,err = p2.communicate()
  ​
  print(out)
  ​
  /dev/vdb1      ext4      493G4.8G463G   2% /data
  /dev/vdd1      ext4   1008G420G537G44% /data1
  /dev/vde1      ext4      985G503G432G54% /data2
  print(err)
  None
  四、总结
  那么我们到底该用哪个模块、哪个函数来执行命令与系统及系统进行交互呢?下面我们来做个总结:
  首先应该知道的是,Python2.4版本引入了subprocess模块用来替换os.system()、os.popen()、os.spawn*()等函数以及commands模块;也就是说如果你使用的是Python 2.4及以上的版本就应该使用subprocess模块了。
  如果你的应用使用的Python 2.4以上,但是是Python 3.5以下的版本,Python官方给出的建议是使用subprocess.call()函数。Python 2.5中新增了一个subprocess.check_call()函数,Python 2.7中新增了一个subprocess.check_output()函数,这两个函数也可以按照需求进行使用。
  如果你的应用使用的是Python 3.5及以上的版本(目前应该还很少),Python官方给出的建议是尽量使用subprocess.run()函数。
  当subprocess.call()、subprocess.check_call()、subprocess.check_output()和subprocess.run()这些高级函数无法满足需求时,我们可以使用subprocess.Popen类来实现我们需要的复杂功能。
页: [1]
查看完整版本: python的subprocess模块