python-subprocess模块用法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/polyhedronx/article/details/82015271

subprocess是用于启动进程,并与进程通信的模块。

·格式

该模块定义了一个Popen类:

class 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):

参数释义:

-args  应该是字符串或程序参数序列,要执行的程序通常是args序列或字符串中的第一项,但可以使用
executable参数显式设置。

   在UNIX 上,当 shell=False(默认), 类Popen 用 os.execvp() 来执行子程序,args通常应该是
一个序列,如果args是一个字符串,它也会被视为只有一个元素的序列。

   在UNIX 上,当 shell=True,如果 args 是字符串,它将作为命令行字符串通过shell 执行;如果是
一个序列,它的第一项将作为命令行字符串,其他项将被视为附加的shell参数。

   在 Windows 上,类Popen 用 CreateProcess() 来执行子程序,它以字符串作为参数。 
   如果args是一个序列,它将使用 list2cmdline 方法转换为字符串。需要注意的是,并非所有MS 
Windows应用程序都以相同的方式解释命令行,list2cmdline 是为使用与MS C运行规则相同的应用程序而设
计的。


- bufsize 如果被赋值,值将作为内建函数 open() 的参数,0意味着无缓冲,1就是行缓冲,任何其他正值
意味着使用与该值大小接近的缓冲区。负bufsize意味着使用系统默认值,这通常意味着完全缓冲。 bufsize
的默认值为0(无缓冲)。


- stdin, stdout and stderr 分别代表子程序的标准输入,标准输出,标准错误输出的文件句柄。
   有效值为PIPE、现有文件描述符(正整数)、现有文件对象和None。若赋值为PIPE ,就会为子程序创
建新管道 ;若为None,不会发生重定向,子程序的文件句柄将从父程序继承。
   另外,stderr可以是STDOUT,这表明子程序的错误数据可以被获得并发送到stdout输出。


-  preexec_fn 如果preexec_fn设置为可调用对象,则在执行子进程之前,将在子进程中调用此对象。


- close_fds 若为true,则在执行子进程之前将关闭除0,1和2之外的所有文件描述符。


- shell 若为true,则将通过shell执行指定的命令。


- cwd 若不是None,在执行子进程之前,当前目录将更改为cwd。


- env 若不是None,它将为新进程指定环境变量。


- universal_newlines 文件对象stdout和stderr作为文本文件打开,但可以通过 '\n' (Unix), '\r' 
(Mac), '\r\n' (Win)断行。所有这些外部表示都被Python程序视为'\n'。注意:仅当Python使用通用换行
支持(默认)构建时,此功能才可用。
   注意这些特征只在python支持通用换行的时候有效(默认支持)。
   此外,communication() 方法不会更新文件对象stdout,stdin和stderr的换行属性。



- startupinfo, creationflags 如果给定,将传递给底层的 CreateProcess() 函数。它可以指定主窗口
的外观和新进程的优先级等内容。(仅限Windows)

    subprocess.startupinfo 详解网址:

    http://www.programcreek.com/python/example/5376/subprocess.STARTUPINFO

该模块还定义了两个快捷功能:

call(*args, **kwargs):

使用参数运行命令。等待命令完成,然后返回returncode属性。

参数与Popen构造函数相同。例:

retcode = call(["ls", "-l"])

··异常

在新程序开始执行之前,子进程中引发的异常将在父进程中重新引发。此外,异常对象将有一个名为'child_traceback'的额外属性,该属性是一个包含来自子进程视点的回溯信息的字符串。

引发的最常见异常是OSError。例如,在尝试执行不存在的文件时会发生这种情况。应用程序应当对OSErrors作出处理。

如果使用无效参数调用Popen,则会引发ValueError。

 

··安全性

与其他一些 popen 函数不同,此实现永远不会隐式调用/bin/sh。这意味着所有字符(包括shell元字符)都可以安全地传递给子进程。

 

··Popen 对象

Popen类的实例具有以下方法:

Popen.poll():

检查子进程是否已终止。终止则返回returncode属性;否则,返回None。

Popen.wait(timeout=None):

等待子进程终止,返回returncode属性。

如果进程在超时秒后没有终止,则引发 TimeoutExpired 异常。捕获此异常并重试等待将会更安全。

注意:当使用stdout = PIPE或stderr = PIPE,并且子进程生成太多输出以致于阻止了等待OS管道缓冲区接受更多数据时,将导致死锁。使用管道时可以使用 Popen.communicate() 来避免这种情况。

注意:该功能使用忙循环(非阻塞呼叫和短暂睡眠)实现。使用asyncio模块进行异步等待:请参阅asyncio.create_subprocess_exec

在Python3.3中添加了timeout参数

从Python3.4开始不推荐使用endtime参数。这是无意中暴露在3.3中,但没有记录,因为它是私人内部使用。请改用timeout 。

Popen.communicate(input=Nonetimeout=None):

与进程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。等待进程终止。可选的input参数应该是要发送到子进程的数据,如果没有数据需要发送给子进程,则设为None。如果在文本(text)模式下打开流,则input参数必须是字符串(string);否则,它必须是字节(bytes)。

communic()返回一个 tuple(stdout_data, stderr_data) 。如果在文本模式下打开流,则数据将是字符串;否则,是字节。

注意:如果要将数据发送到进程的stdin,则需要使用stdin = PIPE创建Popen对象。同样,要在结果元组(tuple)中获取除None之外的任何内容,还需要设置stdout = PIPE and/or stderr = PIPE。

如果进程在超时(timeout)秒后没有终止,则会引发TimeoutExpired异常。捕获此异常并重试通信不会丢失任何输出。

如果超时到期,则子进程不会被终止,因此为了正确清理,行为良好的应用程序应该终止子进程并完成通信:

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

注意:读取的数据缓冲在内存中,因此如果数据大小很大或不受限制,请不要使用此方法。

在Python3.3中添加了timeout参数

Popen.send_signal(signal):

向子进程发送signal信号。

注意:在 Windows 上,SIGTERM和terminate() 的作用相同。可以将 CTRL_C_EVENTCTRL_BREAK_EVENT 发送到使用 creationflags 参数启动的进程,该参数包括 CREATE_NEW_PROCESS_GROUP 。

Popen.terminate():

终止(stop)子进程。在Posix OSs上,该方法将SIGTERM发送给子进程。在Windows上,调用Win32 API函数TerminateProcess() 来停止子进程。

Popen.kill():

杀死子进程。在Posix OSs上,该方法将 SIGKILL 发送给子进程。在Windows上,kill() 和terminate() 的作用相同。

还提供以下属性:

Popen.args:

args参数传递给Popen - 程序参数序列或单个字符串。

Python3.3中的新功能。

Popen.stdin:

如果stdin参数是PIPE,则此属性是open() 返回的可写流对象。如果指定了encoding或errors参数或者universal_newlines参数为True,则流是文本流,否则它是字节流。如果stdin参数不是PIPE,则此属性为None。

   obj.stdin.write(" args ")

Popen.stdout:

如果stdout参数是PIPE,则此属性是open() 返回的可读流对象。从流中读取提供子进程的输出。如果指定了encoding或errors参数或者universal_newlines参数为True,则流是文本流,否则它是字节流。如果stdout参数不是PIPE,则此属性为None。

   obj.stdout.read() 

Popen.stderr:

如果stderr参数是PIPE,则此属性是open() 返回的可读流对象。从流中读取提供子进程的错误输出。如果指定了encoding或errors参数或者universal_newlines参数为True,则流是文本流,否则它是字节流。如果stderr参数不是PIPE,则此属性为None。

警告:使用 communic() 而不是 .stdin.write,.stdout.read 或 .stderr.read 来避免由于任何其他OS管道缓冲区填满和阻止子进程而导致的死锁。

Popen.pid:

子进程的进程ID。

注意:如果将shell参数设置为True,则这是生成的shell的进程ID。

Popen.returncode:

子进程返回码,由poll() 和wait() 设置(间接通过communic())。 None 表示该进程尚未终止,负值 -N 表示子进程被信号 N 终止(仅限POSIX)。

  • returncode=0 表示执行成功
  • returncode=127 表示语句为空串
  • returncode=17 表示找不到表
  • returncode=64 表示缺失关键字
  • returncode=41 表示查询的字段不存在

例如:

import os
import shlex
import signal
import subprocess

shell_cmd = 'ping www.baidu.com'
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# ***CTRL_C信号***
# os.kill(p.pid, signal.CTRL_C_EVENT)
# p.send_signal(signal.CTRL_C_EVENT)
# ***SIGTERM信号***
# p.send_signal(signal.SIGTERM)
# p.terminate()
# p.kill()
while p.poll() is None:  # 当子进程未终止
	line = p.stdout.readline().decode('gbk').strip()
	print(line)
if p.returncode == 0:
	print('Subprogram success')
else:
	print('Subprogram failed')

 

·用 subprocess 模块替换旧函数

在本节中,“a ==> b”表示a可以被b替代。

注意:如果找不到执行的程序,则本节中的所有函数都会或多或少的调用失败。此模块引发OSError异常。

在以下示例中,我们假设 subprocess 模块使用 from subprocess import * 导入。

··替换/bin/sh shell反引号

output=`mycmd myarg`
==>
output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]

··替换shell管道

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

··替换os.system()

sts = os.system("mycmd" + " myarg")
==>
p = Popen("mycmd" + " myarg", shell=True)
sts = os.waitpid(p.pid, 0)

注意:

  • 通常不需要通过shell调用程序。
  • 查看returncode属性比退出状态更容易。

一个更实际的例子:

try:
    retcode = call("mycmd" + " myarg", shell=True)
    if retcode < 0:
        print >>sys.stderr, "Child was terminated by signal", -retcode
    else:
        print >>sys.stderr, "Child returned", retcode
except OSError, e:
    print >>sys.stderr, "Execution failed:", e

··替换os.spawn*

P_NOWAIT示例:

pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
==>
pid = Popen(["/bin/mycmd", "myarg"]).pid

P_WAIT示例:

retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
==>
retcode = call(["/bin/mycmd", "myarg"])

Vector示例:

os.spawnvp(os.P_NOWAIT, path, args)
==>
Popen([path] + args[1:])

Environment示例:

os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
==>
Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})

··替换os.popen*

pipe = os.popen(cmd, mode='r', bufsize)
==>
pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout

pipe = os.popen(cmd, mode='w', bufsize)
==>
pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin


(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
==>
p = Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdin, child_stdout) = (p.stdin, p.stdout)


(child_stdin,
 child_stdout,
 child_stderr) = os.popen3(cmd, mode, bufsize)
==>
p = Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
(child_stdin,
 child_stdout,
 child_stderr) = (p.stdin, p.stdout, p.stderr)


(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
==>
p = Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)

··替换popen2.*

注意:如果popen2函数的cmd参数是一个字符串,则该命令通过 /bin/sh 执行。如果是列表,则直接执行该命令。

(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
==>
p = Popen(["somestring"], shell=True, bufsize=bufsize
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)


(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
==>
p = Popen(["mycmd", "myarg"], bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

popen2.Popen3 和 popen3.Popen4 基本上用作subprocess.Popen,除了:

  • subprocess.Popen如果执行失败则引发异常。
  • capturestderr参数被替换为stderr参数。
  • 必须指定stdin = PIPE和stdout = PIPE。
  • popen2默认关闭所有文件描述符,但必须使用subprocess.Popen指定close_fds = True。

参考:

python的subprocess.Popen()的简单用法

17.5. subprocess — Subprocess management

PEP 324 -- subprocess - New process module

python中使用subprocess.Popen中的返回值总结

Python_cmd的各种实现方法及优劣(subprocess.Popen, os.system和commands.getstatusoutput)

猜你喜欢

转载自blog.csdn.net/polyhedronx/article/details/82015271
今日推荐