需要通过Python去执行一条系统命令或脚本,系统的shell命令是独立于你的python进程之外的,每执行一条命令,就是发起一个新进程,通过python调用系统命令或脚本的模块在python2有os.system。
执行返回命令执行状态,利用echo $? 查看到返回值‘0’
>>> import os >>> os.system('uname -a') Darwin MacBook-Pro.local 17.2.0 Darwin Kernel Version 17.2.0: Fri Sep 29 18:27:05 PDT 2017; root:xnu-4570.20.62~3/RELEASE_X86_64 x86_64 0
python2中除了os.system可以调用系统命令,commands,popen2等也可以,比较乱。于是官方在Python3推出了subprocess,目地是提供统一的模块来实现对系统命令或脚本的调用。
一、subprocess.run()方法
subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs)
首先注意run方法执行结果,有stdout、args、returncode这三个属性。
>>> import subprocess >>> a = subprocess.run(['du', '-sh']) 24K . >>> a.stdout # 拿到标准输出结果 >>> a.args # 拿到标准输出命令 ['du', '-sh'] >>> a.returncode # 拿到标准输出返回值 0
标准写法:subprocess.run(['df', '-h'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True)
>>> a = subprocess.run(['ls', '-lrt'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) >>> a.stdout # 标准输出 b'total 48 -rw-r--r-- 1 hqs staff 51 4 12 16:10 __init__.py -rw-r--r-- 1 hqs staff 16790 4 17 22:38 ftp_client.py ' >>> a.stderr # 标准错误 b'' >>> a = subprocess.run(['df', '-sdfh'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) >>> a.stderr b'df: illegal option -- s usage: df [-b | -H | -h | -k | -m | -g | -P] [-ailn] [-T type] [-t] [filesystem ...] ' >>> a.stdout b''
运用管道借用操作系统内存,实现Python和shell内存交互。拿到shell命令执行结果。
带管道符的情况,不要用列表(解析不了);
shell=True不做解析直接把整个命令交给shell处理;
check=True有错误将直接提示报错。不加的话不提示报错,需要用stderr查看错误。
>>> a = subprocess.run('df -h | grep disk1',stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True) >>> a.stdout b'/dev/disk1s1 233Gi 62Gi 168Gi 28% 871372 9223372036853904435 0% / /dev/disk1s4 233Gi 3.0Gi 168Gi 2% 3 9223372036854775804 0% /private/var/vm ' >>> a = subprocess.run(['df','-ususidih'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=True) 提示出现CalledProcessError报错
二、subprocee.CALL方法
subprocess.call(*popenargs, timeout=None, **kwargs)
应用列表执行命令主要有call()方法和check_call()方法。
应用列表执行命令,并返回结果:check_output()方法。
应用字符串执行命令主要有getstatusoutput()方法和getoutput()方法。
# 执行命令,返回命令执行状态, 0或非0 >>> retcode = subprocess.call(["ls", "-l"]) total 0 drwx------+ 9 huangqiushi staff 288 3 2 13:32 Desktop drwx------+ 4 huangqiushi staff 128 3 1 08:17 Documents ... drwxr-xr-x 7 huangqiushi staff 224 3 2 20:26 PycharmProjects >>> retcode 0 # 执行命令,如果命令结果为0,就正常返回,否则抛异常 >>> subprocess.check_call(["ls","-l"]) 0 # 字符串格式命令 # 接收字符串格式命令,返回元组格式,第1个元素是执行状态,第2个是命令结果 >>> subprocess.getstatusoutput('ls /bin/ls') (0, '/bin/ls') # 接收字符串格式命令,并返回结果(无状态) >>> subprocess.getoutput('ls /bin/ls') '/bin/ls' # 执行命令,并返回结果(注意返回结果,不是打印) >>> res = subprocess.check_output(['du','-sh']) >>> res b' 27G . '
三、subprocess.Popen()方法
Popen方法是最基础的方法,是run()方法和call()方法的底层封装。
常用参数: args:shell命令,可以是字符串或者序列类型(如:list,元组) stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄 preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用 shell:shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析 cwd:用于设置子进程的当前目录 env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
Popen发起一个新进程不影响主程序,run是当前进程执行,Popen是后台执行。
1、Popen调用后的返回对象
Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法:
def sayhi(): print('run...hahah') a = subprocess.Popen('echo $PWD;sleep 2',shell=True,cwd="/tmp",stdout=subprocess.PIPE,preexec_fn=sayhi) a.poll() # 检查子程序是否终止,返回返回值 a.wait() # 等待子程序终止,返回返回值 a.terminate() # 终止所启动的进程 a.kill() # 杀死所启动的进程 a.communicate() # 与启动的进程交互,发送数据到stdin,从stdout接收输出,然后等待任务结束 a.send_signal(signal.xxx) # 发送系统信号 a.pid # 拿到所启动进程的进程号
2、管道操作
>>> def sayhi(): ... print('run...hahah') >>> a = subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE,preexec_fn=sayhi) >>> a.stdout <_io.BufferedReader name=3> >>> a.stdout.read() b'run...hahah ' # cwd设置当前目录 >>> a = subprocess.Popen('echo $PWD;sleep 2',shell=True,cwd="/tmp",stdout=subprocess.PIPE,preexec_fn=sayhi) >>> a.stdout.read() b'run...hahah /private/tmp '
3、pid()方法使用
注意terminate()和kill()方法的区别:终止和杀死
>>> a = subprocess.Popen('sleep 100',shell=True,stdout=subprocess.PIPE) >>> a.pid 14939 >>> a.terminate() # 停掉不强制,进程没有停掉 >>> a = subprocess.Popen('for i in $(seq 1 100);do sleep 1;echo $i >> /tmp/sleep.log;done',shell=True,stdout=subprocess.PIPE) >>> a.pid 15056 >>> a.kill() # 强制杀死进程
4、communicate()方法
与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束需要用bytes进行交互、且只能交互一次。
>>> a = subprocess.Popen('python3 guessAge.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True) >>> a.communicate(b'1234') # 需要和bytes进行交互 (b'guess age: try smaller,you have 3 chance guess age: ', b'Traceback (most recent call last): File "guessAge.py", line 19, in <module> guess_age = int(input("guess age: ")) EOFError: EOF when reading a line ')
5、send_signal()方法发送系统信号
import signal a = subprocess.Popen('sleep 100',stdout=subprocess.PIPE,shell=True) a.send_signal(signal.SIGKILL)