使用subprocess模块调用系统命令

一、os.system(commandString)

import os
statusCode=os.system("powershell sleep 3 ;echo 天下大势为我所控")
print("over",statusCode)
  • 这里使用了powershell来执行sleep命令,在cmd里面是没有sleep命令的。
  • 会发现os.system(commandString)是阻塞的。
  • 这个函数类似C语言里面的stdlib.h中的system命令
  • 这种方法只负责运行程序并获取程序结束状态值,无法获取输出,输出会直接打印到stdout,所以要想获取输出也可以重定向一下stdout,然而没有这个必要,因为可以用下面的subprocess方法。

这种方法只在确保命令不会出错并且需要阻塞的情况下使用。

二、os.popen(commandString)

这种方法已经不鼓励使用了,可以跳过。

import os
pipe=os.popen("powershell sleep 3 ;echo 天下大势为我所控")
s=pipe.read()
print(s)

这种方法直接接受运行的输出结果,缺点是无法获取程序的结束状态。os.system()只获取状态不获取输出,os.popen()只获取输出不获取状态。这两者都是历史糟粕,不必再看。subprocess功能丰富灵活,是更好的选择。

三、使用commands模块

这个模块在Python3中已经废弃了

import commands
(status, output) = commands.getstatusoutput('cat /proc/cpuinfo')
print status,output

四、鼓励使用:subprocess

4.1简介

前面介绍的几种方法都存在各种问题,以后用subprocess就对了。它用于替换以下函数(包括上面介绍的函数):

os.system
os.spawn*
os.popen*
popen2.*
commands.*

spawn这个单词意思是“鱼、蛙等大量产卵”,在计算机领域中表示一个线程创建另一个线程。

4.2 subprocess模块的三个方法

  • subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)->statusCode
    利用stdin、stdout、stderr可以重定向,shell=True可以使用更丰富的命令,此方法阻塞直到程序返回,返回结果为退出码。
  • subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)->statusCode
    此方法和call非常像,唯一的区别是,当程序退出码不为0时,抛出CalledProcessError异常,而不是让用户手动判断状态码。
  • subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)->outputString
    此方法和check_call非常像,唯一的区别是它的返回值是进程的stdout中的内容,因此参数中没有stdout参数。

这三个方法共同点是它们都是阻塞的。

官网上有一个警告,说指定它们的stdout和stderr容易造成死锁。
Note Do not use stdout=PIPE or stderr=PIPE with this function as that can deadlock based on the child process output volume. Use Popen with the communicate() method when you need pipes.

举例:

import subprocess
statusCode=subprocess.call("echo one",shell=True)#相当于system命令,只返回状态值,并打印输出到stdout
print(statusCode)
statusCode=subprocess.check_call(["echo","  two"],shell=True)
print(statusCode)
output=subprocess.check_output("echo three",shell=True)#只获取output
print(output)

4.3 常用参数说明

  • args参数
    args参数可以是两种类型中的任意一种:字符串和字符串数组。使用字符串数组是更好的选择,因为参数中可能包含空白字符(例如cat "ha ha.txt"),字符串数组比直接传入字符串要安全一些。注意:当args为一个字符串时,需要满足两个条件:(1)args必须是可执行程序的名字(无法为它指定任何参数);(2)shell=True。这两个条件有一个满足,args就可以传入字符串。
  • stdin,stdout,stderr
    可取值为PIPE、文件描述符(int类型)、file类型的对象、None。
  • universal_newlines
    此值为True表示stdout和stderr的换行符一律采用\n

4.4 CalledProcessError

在上述三个主要函数中,check_call()和check_output()可能会抛出CalledProcessError错误。CalledProcessError错误包括以下三个属性:

  • returncode:Exit status of the child process.
  • cmd:Command that was used to spawn the child process.
  • output:Output of the child process if this exception is raised by check_output(). Otherwise, None.

4.5 Popen类

如果觉得以上三个方法不够灵活,就使用Popen类。创建Popen类之后,子进程就开始运行了,并不会阻塞当前进程。
上述三个方法都是对Popen类的封装,Popen类的构造函数的参数跟上述三个函数有重合的部分,此外,还包括一下参数:

  • preexec_fn
    预先执行的函数,在执行命令之前可以执行preexec_fn进行一些初始化
  • close_fds
    if close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed. (Unix only). Or, on Windows, if close_fds is true then no handles will be inherited by the child process. Note that on Windows, you cannot set close_fds to true and also redirect the standard handles by setting stdin, stdout or stderr.
  • cwd
    程序运行的当前目录
  • env
    若env为None,默认集成父进程的环境变量;否则env应该是一个字典。

Popen对象包含丰富的函数和属性:

  • poll():检查子进程是否执行完毕,不阻塞
  • wait(timeout):等待子进程一段时间,如果子进程在时限内运行结束,正常向下执行。否则抛出超时异常。
  • communicate(input)->(stdoutContent,stderrContent):和子进程进行交互,主进程向子进程stdin写入input这样的字符串,函数返回子进程当前的输出内容和错误内容(直到读到底为止)。此函数对子进程的stdin、stdout、stderr有特殊要求。
  • send_signal(),terminate(),kill():向子进程发送SIGTERM、SIGKILL之类的信号。
  • stdin,stdout,stderr:Popen对象的三个属性
  • pid:Popen对象的进程ID
  • returnCode:Popen对象的状态码

例1:

import subprocess

obj = subprocess.Popen("mkdir t3", shell=True, cwd='/home/dev',)     #在cwd目录下执行命令

例2:

import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")
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)

例3:

import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n")
obj.stdin.write("print(2)")

out_error_list = obj.communicate()
print(out_error_list)
import subprocess

obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
out_error_list = obj.communicate('print("hello")')
print(out_error_list)

4.6 使用Popen可能会遇到的问题

(1)windows平台下虽然命令不带.cmd、.exe也能够运行,但是在使用Popen时必须指定全名,否则会找不到可执行文件。
(2)当shell参数为True时,process.kill()和process.terminate()函数仅仅杀死了shell进程,而shell进程的子进程却还活着,这样就产生了许多僵尸进程,正确的解决方法是:杀死一个组进程。

pro = subprocess.Popen(s, shell=True,preexec_fn=os.setsid)#使用setsid函数初始化进程,setsid函数的用途是创建新的进程组,否则最后关闭一组进程时把自己也杀掉了
try:
    pro.wait(1)  # 最多等待1秒钟
except Exception as ex:
    print("over time ", pro.pid)
    os.killpg(os.getpgid(pro.pid), signal.SIGTERM)  # 杀死一个进程组
    raise ex

参考资料

官网教程
https://docs.python.org/2/library/subprocess.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值