|
🚀 个人主页:xmp65535🚀 专栏:python技术专栏目录前言一、subprocess模块概述为什么要使用subprocess模块?二、subprocess的常用函数1.subprocess.run()2.subprocess.Popen()3.subprocess.call()4.subprocess.check_call()5.subprocess.check_output()6.subprocess.getstatusoutput()7.subprocess.getoutput()三、subprocess模块的高级用法1.与子进程的流交互2.使用管道3.处理超时前言在实际开发过程中,我们经常会遇到需要从Python脚本中调用外部程序或脚本的场景。这时候,Python的subprocess模块就显得尤为重要。它是Python用来生成子进程、连接到它们的输入/输出/错误管道,以及获取它们的返回码的一个模块。本文将带你深入了解subprocess模块,学会如何高效地使用它来扩展你的Python脚本功能。一、subprocess模块概述subprocess模块是Python标准库的一部分,它为创建和管理子进程提供了强大的接口。在Python2.4中引入以取代旧的模块(如os.system和os.spawn*等),并提供了更复杂的进程管理功能,比如超时控制、I/O管道通信等。为什么要使用subprocess模块?安全性:与os.system相比,subprocess避免了shell注入攻击的风险。灵活性:subprocess可以与子进程的stdin、stdout和stderr流进行交互。功能丰富:它支持复杂的系统调用,如管道和重定向。二、subprocess的常用函数1.subprocess.run()subprocess.run() 是Python3.5引入的一个高级接口,用于替代旧的 subprocess.call() 和 subprocess.check_output() 等函数。它可以执行命令并等待其完成,然后返回一个 CompletedProcess 对象,包含执行结果的信息,如返回码、标准输出和标准错误等。subprocess.run() 提供了更多的参数选项,如可以指定输入数据、设置超时时间、指定工作目录等。参数说明:args:这是命令和参数的列表,其中命令是列表的第一个元素。如果shell参数设置为True,args必须是一个字符串stdin:可以是None、一个已打开的文件描述符、一个现有的文件对象,或者subprocess.PIPE。用于指定子进程的标准输入。input:如果stdin参数是PIPE,这个参数可以被用来传递一个字符串到子进程的标准输入。stdout和stderr:这些参数与stdin类似,但它们控制的是标准输出和标准错误输出。它们也可以被设置为None、一个文件描述符、一个现有的文件对象,或者subprocess.PIPE。capture_output:如果设置为True,stdout和stderr将会被捕获。这相当于设置stdout=subprocess.PIPE和stderr=subprocess.PIPE。shell:如果为True,指定的命令将通过shell执行。cwd:如果指定,子进程的当前工作目录将被改变到cwd。timeout:如果指定,且执行时间超过了这个值(以秒计),将会抛出subprocess.TimeoutExpired异常。check:如果设置为True,并且进程以非零状态码退出,将会抛出subprocess.CalledProcessError异常。encoding和errors:指定如何解码从子进程输出的字节。只有当stdout或stderr被捕获时才有效。text:一个简写的标志,用于设置encoding='utf-8'和universal_newlines=True,它会将stdout和stderr的输出作为字符串处理。env:指定子进程的环境变量。如果为None,使用父进程的环境变量。universal_newlines:如果为True,stdin、stdout和stderr将会作为文本流处理,类似于text=True。**other_popen_kwargs:你可以提供其他的关键字参数,这些参数将直接传递给Popen构造函数。示例:importsubprocesstry:result=subprocess.run(['ls','-l','/nonexistent'],capture_output=True,text=True,check=True)print(result.stdout)exceptsubprocess.CalledProcessErrorase:print('Failedtoruncommand:',e)'运行运行2.subprocess.Popen()subprocess.Popen() 是一个更底层的接口,用于在新的进程中执行命令。它返回一个 open 对象,可以用于控制和管理子进程的执行过程,如向子进程发送输入数据、读取子进程的输出、等待子进程结束等。相比于其他函数,subprocess.Popen() 提供了更多的灵活性和控制。参数说明:args: 和 run 函数相同。bufsize:缓冲区大小,-1表示使用系统默认缓冲区大小,0表示不使用缓冲区,1表示行缓冲。executable:如果指定,将使用这个可执行文件来替换要执行的程序。stdin, stdout, stderr, shell, cwd:与 run 函数相同。preexec_fn:仅在Unix系统上有效)是一个可调用对象,它将在子进程运行之前被调用。close_fds:如果为True(默认值),在子进程中除了0、1和2之外所有的文件描述符都将被关闭。env:用于指定环境变量。shell:如果这个参数为True,指定的命令将通过shell执行。cwd:如果指定,子进程的工作目录将改变到cwd。env:用于指定子进程的环境变量。如果为None,子进程会继承父进程的环境变量。universal_newlines:(现在推荐使用text参数)如果为True,stdin、stdout和stderr将作为文本流(字符串)而不是字节流。startupinfo和creationflags:这两个参数仅在Windows系统上有效,用于控制子进程的创建方式。restore_signals:(仅在Unix系统上有效)如果为True,在执行新程序前,将会恢复Python的信号处理为默认值。start_new_session:(仅在Unix系统上有效)如果为True,子进程将会在新的会话中启动。pass_fds:(仅在Unix系统上有效)要传递给子进程的文件描述符。encoding和errors:这些参数用于设置在解码和编码文本数据时使用的编码和错误处理方式。text:一个用于设置编码为"utf-8"且universal_newlines=True的简化参数,这将会让stdout和stderr的输出作为字符串处理。示例:importsubprocessprocess=subprocess.Popen(['ping','-c','4','example.com'],stdout=subprocess.PIPE,text=True)stdout,stderr=process.communicate()print(stdout)'运行运行Popen类有一些其他的方法供进阶使用,例如poll(),wait(),communicate()等。poll():检查子进程是否已经结束,返回 returncode 属性。wait():等待子进程结束,并返回 returncode 属性。communicate(input=None,timeout=None):与子进程进行交互,发送数据到stdin,并从stdout和stderr读取数据,然后等待子进程结束。示例:importsubprocessprocess=subprocess.Popen(['sleep','2'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)print(process.poll())#None,因为进程还未结束output,error=process.communicate()#等待进程结束,并获取输出print(process.poll())#返回码,通常为0,表示成功'运行运行subprocess.PIPEsubprocess.PIPE可以被用作Popen函数的stdin/stdout/stderr参数的值,它表示需要创建一个新的管道到子进程。示例:importsubprocess#创建新的管道,捕获输出withsubprocess.Popen(['ls','-l'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)asprocut,err=proc.communicate()print(out.decode())'运行运行3.subprocess.call()这个函数用于执行指定的命令,等待命令执行完成,并返回命令的返回码。参数基本上与subprocess.run()相同。示例:importsubprocessreturn_code=subprocess.call(["echo","Hello,World!"])print("Commandreturned:",return_code)'运行运行4.subprocess.check_call()这个函数与call()类似,但如果执行的命令返回非0退出码,则会抛出CalledProcessError。参数与subprocess.call()相同。示例:importsubprocesstry:subprocess.check_call(["false"])#这将抛出异常,因为'false'命令的退出码不为0exceptsubprocess.CalledProcessErrorase:print('Error:',e)'运行运行5.subprocess.check_output()subprocess.check_output() 用于执行命令并返回其标准输出。如果命令执行失败或返回非零的返回码,则会抛出 CalledProcessError 异常。参数基本上与subprocess.run()相同。示例:importsubprocesstryutput=subprocess.check_output(["echo","Hello,World!"])print(output.decode())#输出需要解码exceptsubprocess.CalledProcessErrorase:print('Error:',e)'运行运行6.subprocess.getstatusoutput()这个函数在Unix上执行带有{cmd;}2>&1的shell命令,并返回(status,output)元组。示例:importsubprocessstatus,output=subprocess.getstatusoutput('ls/nonexistent')print('Status:',status)print('Output:',output)'运行运行这个命令会尝试列出一个不存在的目录,因此它将返回一个错误状态和错误信息。7.subprocess.getoutput()这个函数用于执行命令并获取它的输出(stdout),不返回返回码。示例:importsubprocessoutput=subprocess.getoutput('echoHello,World!')print('Output:',output)'运行运行这个命令会打印Hello,World!到stdout,并捕获这个输出。三、subprocess模块的高级用法1.与子进程的流交互subprocess模块允许你启动新的进程,连接到它们的输入/输出/错误管道,并获取它们的返回码。这个模块提供了很多与子进程进行交互的功能,Popen类是其中的核心。使用Popen构造函数,你可以指定进程的输入、输出、错误管道,以及许多其他的选项。例如,你可以使用Popen类并将stdin,stdout,和stderr参数设置为subprocess.PIPE,这样就可以与子进程的输入和输出流进行交互:importsubprocess#启动进程proc=subprocess.Popen(['sort'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)#向子进程发送数据并获取输出output,errors=proc.communicate(input=b'one\ntwo\nthree\nfour\nfive\n')#打印排序后的输出数据print(output.decode())#输出将会是排序后的列表'运行运行在这个例子中,communicate()方法用于发送数据到子进程的stdin,并从stdout和stderr获取数据。2.使用管道在Unix和Windows系统中,可以使用subprocess模块创建管道。这通常是通过将一个进程的stdout与另一个进程的stdin相连来完成的。importsubprocess#创建一个管道:grep通过ps获取的进程列表ps=subprocess.Popen(('ps','aux'),stdout=subprocess.PIPE)grep=subprocess.Popen(('grep','python'),stdin=ps.stdout,stdout=subprocess.PIPE)ps.stdout.close()#允许ps释放资源output=grep.communicate()[0]print(output.decode())'运行运行在这个例子中,ps命令的输出被直接连接到了grep命令的输入。我们通过关闭ps.stdout来确保ps可以接收到SIGPIPE信号,如果grep先完成。3.处理超时subprocess.run()函数包括一个timeout参数,可以用来限制子进程的运行时间。如果超时,将会抛出一个TimeoutExpired异常。importsubprocesstry:#运行命令,最多允许执行1秒subprocess.run(['sleep','2'],timeout=1)exceptsubprocess.TimeoutExpired:print('Thesubprocessdidnotfinishwithinthetimeoutperiod!')'运行运行在这个例子中,我们尝试运行一个会睡眠2秒的子进程,但是设置了1秒的超时。因为子进程执行时间超过了超时设置,所以TimeoutExpired异常被抛出。
|
|