python 学习第十九天(subprocess模块)

 

  本文主要介绍 subprocess 模块及其提供的 Popen 类,以及如何使用该构造器在一个进程中创建新的子进程。此外,还会简要介绍 subprocess 模块提供的其他方法与属性,这些功能上虽然没有 Popen 强大的工具,在某些情况下却非常方便和高效。

  本文的目录如下:

  1. subprocess.Popen 类

  2. Popen 对象的属性

  3. Popen 对象的方法

  4. subprocess模块的其他简便方法

  5. subprocess模块的其他属性

  6. subprocess模块定义的异常

 

subprocess.Popen 类

  通过调用:

subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

  创建并返回一个子进程,并在这个进程中执行指定的程序。

  实例化 Popen 可以通过许多参数详细定制子进程的环境,但是只有一个参数是必须的,即位置参数 args ,下面也会详细介绍剩余的具名参数。

参数介绍:

  • args:要执行的命令或可执行文件的路径。一个由字符串组成的序列(通常是列表),列表的第一个元素是可执行程序的路径,剩下的是传给这个程序的参数,如果没有要传给这个程序的参数,args 参数可以仅仅是一个字符串。
  • bufsize:控制 stdin, stdout, stderr 等参数指定的文件的缓冲,和打开文件的 open()函数中的参数 bufsize 含义相同。
  • executable:如果这个参数不是 None,将替代参数 args 作为可执行程序;
  • stdin:指定子进程的标准输入;
  • stdout:指定子进程的标准输出;
  • stderr:指定子进程的标准错误输出;

  对于 stdin, stdout 和 stderr 而言,如果他们是 None(默认情况),那么子进程使用和父进程相同的标准流文件。

  父进程如果想要和子进程通过 communicate() 方法通信,对应的参数必须是 subprocess.PIPE(见下文例4);

  当然 stdin, stdout 和 stderr 也可以是已经打开的 file 对象,前提是以合理的方式打开,比如 stdin 对应的文件必须要可读等。 

  • preexec_fn:默认是None,否则必须是一个函数或者可调用对象,在子进程中首先执行这个函数,然后再去执行为子进程指定的程序或Shell。
  • close_fds:布尔型变量,为 True 时,在子进程执行前强制关闭所有除 stdin,stdout和stderr外的文件;
  • shell:布尔型变量,明确要求使用shell运行程序,与参数 executable 一同指定子进程运行在什么 Shell 中——如果executable=None 而 shell=True,则使用 /bin/sh 来执行 args 指定的程序;也就是说,Python首先起一个shell,再用这个shell来解释指定运行的命令。
  • cwd:代表路径的字符串,指定子进程运行的工作目录,要求这个目录必须存在;
  • env:字典,键和值都是为子进程定义环境变量的字符串;
  • universal_newline:布尔型变量,为 True 时,stdout 和 stderr 以通用换行(universal newline)模式打开,
  • startupinfo:见下一个参数;
  • creationfalgs:最后这两个参数是Windows中才有的参数,传递给Win32的CreateProcess API调用。

  同 Linux 中创建子进程类似,父进程创建完子进程后,并不会自动等待子进程执行,父进程在子进程之前推出将导致子进程成为孤儿进程,孤儿进程统一由 init 进程接管,负责其终止后的回收工作。

  如果父进程在子进程之后终止,但子进程终止时父进程没有进行最后的回收工作,子进程残留的数据结构称为僵尸进程。大量僵尸进程将耗费系统资源,因此父进程及时等待和回收子进程是必要的,除非能够确认自己比子进程先终止,从而将回收工作过渡给 init 进程。

  这个等待和回收子进程的操作就是wait()函数,下文中将会介绍。

例1:

  创建一个子进程,然后执行一个简单的命令




1
2
3
4
5
6
7
8
9
10
11
>>>  import  subprocess
>>> p  =  subprocess.Popen( 'ls -l' , shell = True )
>>> total  164
- rw - r - - r - -   1  root root    133  Jul   4  16 : 25  admin - openrc.sh
- rw - r - - r - -   1  root root    268  Jul  10  15 : 55  admin - openrc - v3.sh
...
>>> p.returncode
>>> p.wait()
0
>>> p.returncode
0




  这里也可以使用 p = subprocess.Popen([‘ls’, ‘-cl’]) 来创建子进程。

 

Popen 对象的属性

  Popen创建的子进程有一些有用的属性,假设 p 是 Popen 创建的子进程,p 的属性包括:

1. 

p.pid

  子进程的PID。

 2. 

p.returncode

  该属性表示子进程的返回状态,returncode可能有多重情况:

  • None —— 子进程尚未结束;
  • ==0 —— 子进程正常退出;
  • > 0—— 子进程异常退出,returncode对应于出错码;
  • < 0—— 子进程被信号杀掉了。

 3. 

p.stdin, p.stdout, p.stderr

  子进程对应的一些初始文件,如果调用Popen()的时候对应的参数是subprocess.PIPE,则这里对应的属性是一个包裹了这个管道的 file 对象,

  

Popen 对象的方法

1.

p.poll()

  检查子进程  p 是否已经终止,返回 p.returncode 属性 (参考下文 Popen 对象的属性);

2.

p.wait()

  等待子进程 p 终止,返回 p.returncode 属性;

  注意:

    wait() 立即阻塞父进程,直到子进程结束!

3.

p.communicate(input=None)

  和子进程 p 交流,将参数 input (字符串)中的数据发送到子进程的 stdin,同时从子进程的 stdout 和 stderr 读取数据,直到EOF。

  返回值:

    二元组 (stdoutdata, stderrdata) 分别表示从标准出和标准错误中读出的数据。

  父进程调用 p.communicate() 和子进程通信有以下限制:

  (1) 只能通过管道和子进程通信,也就是说,只有调用 Popen() 创建子进程的时候参数 stdin=subprocess.PIPE,才能通过 p.communicate(input) 向子进程的 stdin 发送数据;只有参数 stout 和 stderr 也都为 subprocess.PIPE ,才能通过p.communicate() 从子进程接收数据,否则接收到的二元组中,对应的位置是None。

  (2)父进程从子进程读到的数据缓存在内存中,因此commucate()不适合与子进程交换过大的数据。

  注意:

    communicate() 立即阻塞父进程,直到子进程结束!

4.

p.send_signal(signal)

  向子进程发送信号 signal;

5.

p.terminate()

  终止子进程 p ,等于向子进程发送 SIGTERM 信号;

6.

p.kill()

  杀死子进程 p ,等于向子进程发送 SIGKILL 信号;

 

subprocess模块的其他方法

1. 

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

  父进程直接创建子进程执行程序,然后等待子进程完成

  返回值:

    call() 返回子进程的 退出状态 即 child.returncode 属性;

2. 

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

  父进程直接创建子进程执行程序,然后等待子进程完成,具体可以使用的参数,参考上文 Popen 类的介绍。

  返回值:

    无论子进程是否成功,该函数都返回 0;但是 

  如果子进程的退出状态不是0,check_call() 抛出异常 CalledProcessError,异常对象中包含了 child.returncode 对应的返回码。

例2:

  check_call()正常与错误执行命令




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> p  =  subprocess.check_call([ 'ping'  , '-c' '2' 'www.baidu.com' ])
PING www.a.shifen.com ( 220.181 . 111.188 56 ( 84 ) bytes of data.
64  bytes  from  220.181 . 111.188 : icmp_seq = 1  ttl = 42  time = 37.4  ms
64  bytes  from  220.181 . 111.188 : icmp_seq = 2  ttl = 42  time = 37.3  ms
 
- - -  www.a.shifen.com ping statistics  - - -
2  packets transmitted,  2  received,  0 %  packet loss, time  1001ms
rtt  min / avg / max / mdev  =  37.335 / 37.410 / 37.486 / 0.207  ms
>>>  print  p
<strong> 0 < / strong>
>>> p  =  subprocess.check_call([ 'ping'  , '-c' '4' 'www.!@$#@!(*^.com' ])
ping: unknown host www.!@$ #@!(*^.com
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
   File  "/usr/lib/python2.7/subprocess.py" , line  540 in  check_call
     raise  CalledProcessError(retcode, cmd)
subprocess.<strong>CalledProcessError< / strong>: Command  '[' ping ', ' - c ', ' 4 ', ' www.!@$ #@!(*^.com']' returned non-zero exit status 2
>>>  print  p
<strong> 0
< / strong>




  

3.

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)

  父进程直接创建子进程执行程序,以字符串的形式返回子进程的输出。

  返回值:

    字符串形式的子进程的输出结果,但是,

  如果子进程的 退出状态 不是0,那么抛出异常 CalledProcessError,异常对象中包含了 child.returncode 对应的返回码。

  注意:

    check_output() 的函数签名中没有参数 stdout,调用该方法时,子进程的输出默认就返回给父进程。

例3:

  check_output() 调用的子进程正常与错误退出




1
2
3
4
5
6
7
>>> subprocess.check_output([ "echo" "Hello World!" ])
'Hello World!\n'
 
>>> subprocess.check_output( "exit 1" , shell = True )
Traceback (most recent call last):
    ...
subprocess.CalledProcessError: Command  'exit 1'  returned non - zero exit status  1




 

注意:

  使用上面提到的三个方法:call()、check_call() 和 check_output() 时,尽量不要将参数 stderr 和 stdout 设置为 subprocess.PIPE,这几个函数默认都会等待子进程完成,子进程产生大量的输出数据如果造成管道堵塞,父进程再等待子进程完成可能造成死锁。

  

subprocess模块的其他属性

subprocess.PIPE

  调用本模块提供的若干函数时,作为 std* 参数的值,为标准流文件打开一个管道。

例4:

  使用管道连接标准流文件




1
2
3
4
5
6
7
import  subprocess
child1  =  subprocess.Popen([ 'ls' '-l' ], stdout = subprocess.PIPE)
child2  =  subprocess.Popen([ 'wc' '-l' ], stdin = child1.stdout, stdout = subprocess.PIPE)
out  =  child2.communicate()
child1.wait()
child2.wait()
print (out)




  这里将子进程 child1 的标准输出作为子进程 child2 的标准输入,父进程通过 communicate() 读取 child2 的标准输出后打印。

 

subprocess.STDOUT

  调用本模块提供的若干函数时,作为 stderr 参数的值,将子进程的标准错误输出打印到标准输出。

 

subprocess模块定义的异常

exception subprocess.CalledProcessError

  (1)什么时候可能抛出该异常:调用 check_call() 或 check_output() ,子进程的退出状态不为 0 时。

  (2)该异常包含以下信息:

  • returncode:子进程的退出状态;
  • cmd:创建子进程时指定的命令;
  • output:如果是调用 check_output() 时抛出的该异常,这里包含子进程的输出,否则该属性为None。

 

  总结

  本文介绍了Python subprocess的基本用法,使用 Popen 可以在Python进程中创建子进程,如果只对子进程的执行退出状态感兴趣,可以调用 subprocess.call() 函数,如果想通过异常处理机制解决子进程异常退出的情形,可以考虑使用 subprocess.check_call() 和 subprocess.check_output。如果希望获得子进程的输出,可以调用 subprocess.check_output(),但 Popen() 无疑是功能最强大的。

  subprocess模块的缺陷在于默认提供的父子进程间通信手段有限,只有管道;同时创建的子进程专门用来执行外部的程序或命令。

  Linux下进程间通信的手段很多,子进程也完全可能从创建之后继续调用




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本
### 回答1: subprocess 模块Python 标准库中用于启动新的进程的模块。它可以用于执行外部命令、获取进程的输出、向进程发送输入、等待进程结束等操作。通过使用 subprocess 模块,可以在 Python 代码中更方便地控制外部程序,而不必编写额外的 shell 脚本。 ### 回答2: Pythonsubprocess模块是用于创建和管理新的子进程的模块。它提供了一个简单而灵活的方式来执行其他程序,并且可以与外部命令进行交互。 使用subprocess模块,我们可以启动一个新的进程,执行外部命令,并获取其输出。这对于需要执行其他语言编写的程序或者操作系统命令非常有用。通过subprocess模块,我们可以轻松地调用外部命令,并获取其标准输出、标准错误输出,以及执行的返回码。 subprocess模块提供了多种方法来创建并管理子进程,例如: - `subprocess.run()`:执行一个命令,等待其完成并返回运行结果。 - `subprocess.Popen()`:启动一个子进程,并返回一个Popen对象,可以通过该对象与子进程进行交互。 - `subprocess.call()`:执行一个命令,并等待其完成。与`subprocess.run()`类似,不过返回的是命令的返回码。 此外,subprocess模块还提供了一些方便的函数和常量,例如: - `subprocess.check_output()`:执行一个命令,并返回其标准输出。 - `subprocess.PIPE`:表示子进程的标准输入、输出和错误输出。 总而言之,subprocess模块是一个在Python中调用和管理外部命令和程序的强大工具。无论是执行其他语言编写的程序,还是与操作系统命令进行交互,它都提供了便捷的功能和灵活的方法。 ### 回答3: Python subprocess模块提供了一个简便的接口,可以在Python程序中启动和控制新的子进程。它允许我们在代码中运行外部命令,并可以通过输入和输出进行交互。 使用subprocess模块,我们可以执行系统命令、启动其他可执行文件、调用命令行工具等。它提供了多种方法,可以满足不同情况下的需求。 subprocess模块中最常用的函数是`subprocess.run()`。使用该函数可以方便地执行外部命令,并等待其完成。我们可以指定命令和参数,还可以设置输入和输出的方式。 在使用`subprocess.run()`函数时,可以通过`capture_output`参数捕获命令的输出,通过`input`参数传递输入数据。这使得我们可以在Python程序中方便地处理子进程的输出和输入。 `subprocess`模块还提供了其他函数,例如`subprocess.call()`、`subprocess.check_output()`等,可以实现不同的功能。另外,还可以通过`subprocess.Popen()`类来更灵活地控制子进程。 总而言之,Python subprocess模块提供了一个简单、方便和强大的接口,可以在Python程序中执行和控制子进程。它使得我们可以轻松地与外部命令进行交互,并处理输入输出。因此,subprocess模块在编写Python脚本时非常实用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值