python是一门简洁灵活的语言,也是一门胶水语言,能与很多其他的编程语言进行交互,虽然性能差一点,但易用,入门和上手都比较简单,所以一直以来都被使用广泛。
python天生的优势,用它来开发一些devops的自动化作业是非常方便的,当然在linux上,一般我们用shell就能写一些简单的自动化脚本,但如果自动化作业复杂的话,使用shell脚本就很难搞定了,一方面shell脚本量变大就会比较难以工程化,维护和阅读,另外一个重要的原因是shell不具备正经编程语言所具备的丰富的一些类库,比如说map类型必须得bash版本4.x以上才有,或者有序list什么的也没有,这个时候我们就可以用python来搞定了,python能与shell直接交互,所以两者结合,相互取长补短,就非常适合开发各种自动化作业了。
下面我们看如何使用python与shell交互:
方式一:
使用os.system方法
import osos.system("ls")
保存到一个python文件xxx.py里面执行 python xxx.py,或者直接在linux终端上执行
python -c "import os; myCmd = 'ls '; os.system(myCmd)"
这种方式是最原始的方式,能直接调用shell命令,但是没法获取输出结果,所以仅仅适合一些简单的场景
方式二:
使用os.popen方法
import osstream = os.popen('echo 123')output = stream.read()print output
输出123
我们可以看到popen方法可以获取命令执行后到输入结果,但这还是有缺陷的,我们知道在linux里面,有标准的0,1,2来代表标准输入,输出和错误,现在只有stdout没有其他的两个变量还是有限制的,如果仔细看python的方法,会发现popen还有popen2,popen3,popen4同名方法,没错就是后面的版本里面完善了linux的标准输入,输出,错误等信息。
具体点:popen 支持 stdout popen2 支持stdin, stdout popen3 支持stdin, stdout, stderr popen4 支持stdin, stdout and stderr
方式三:看到上面这些这么繁琐的调用,后面就又有了最强大的subprocess模块,subprocess模块的出现是用来替代OS模块中的system()和popen()方法的,官方推荐的是只用subprocess模块来执行系统命令,subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。
Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。
class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0,restore_signals=True, start_new_session=False, pass_fds=(),*, encoding=None, errors=None)
参数介绍如下:args:shell命令,可以是字符串或者序列类型(如:list,元组) bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。0:不使用缓冲区 1:表示行缓冲,仅当universalnewlines=True时可用,也就是文本模式 正数:表示缓冲区大小 负数:表示使用系统默认的缓冲区大小。stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄 preexecfn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用 shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。cwd:用于设置子进程的当前目录。env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。
一个简单的例子:
import subprocess
p = subprocess.Popen('ls -lh', shell=True)p.wait()
print p.returncode
几个主要方法,介绍:
poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。wait(timeout): 等待子进程终止。communicate(input,timeout): 和子进程交互,发送和读取数据。send_signal(singnal): 发送信号到子进程 。terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。kill(): 杀死子进程。发送 SIGKILL 信号到子进程。
这里我已经封装好了2个通用方法,一个是执行命令能够实时获取终端输出的信息,一个是可以执行命令结束后得到结果。
可以实时获取输出的:
import subprocessimport shlexdef real_run_command(command): process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if output: print output.strip() rc = process.poll() return rc
为了能够模拟效果,我写了一个简单的shell脚本:test.sh
for i in `seq 1 4`dosleep 2echo ${i}done
这个脚本每输出一个数字都会sleep 2秒,用ptyhon调用这个脚本,能够实时在终端上看到和shell一样的效果:
rc = real_run_command("sh test.sh")print 'retrurn code=%s'% rc
非实时获取输出的,这个脚本适合大部分场景:
def normal_run_command(cmd): try: child = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = child.communicate() return_code = child.returncode if stdout: print stdout if stderr: print stderr if return_code != 0: print "execute error, return_code=%s " % return_code sys.exit(1) except Exception as e: print "execute cmd=%s occur error %s" % (cmd, e) sys.exit(1) return stdout, stderr, return_code
ok,同样的调用脚本,这个函数会等整个shell执行结束后,输出结果:
out, err, code= normal_run_command("sh xs.sh")print out
至此,我们已经介绍完了如何使用python和shell交互的方式,通过这种方式我们可以很轻松开发一个自动化作业,比如系统参数初始化,使用yum安装各种软件,各种业务项目的部署,启动,停止等等等等,有很多场景都可以发挥。