使用python开发执行oracle sql的自动化执行脚本,一般可以使用oracle官方提供的python包:cx_Oracle。但有些情况下,目标主机上如果没有安装此工具,就需要使用最原始的手段编写python和oracle数据库交互的工具。
这个工具时通过python中的subprocess模块实现的,subprocess时一个子进程管理类,它允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码,而这个模块的底层的进程创建与管理由Popen类处理。
可以把创建一个Popen对象理解为建立了一个子进程,然后可以给这个子进程传递执行命令或函数,并从中获得执行结果。
在python中使用subprocess模块,通过shell和oracle数据库进行交互,具体流程可以总结为以下几步:
1,直接用oracle用户执行脚本,或使用root执行脚本,在脚本中通过os.setuid函数,将当前执行的用户切换到oracle
2,获取ORACLE_HOME环境变量,很多环境并不会在.bash_profile中配置环境变量,所以执行sqlplus时需要提前声明。export ORACLE_SID=xxx
3,使用sqlplus 登录数据库,如果允许,可以使用sysdba 登录,并且使用参数-S,静默登录:sqlplus -S / as sysdba
cmd =
'''export ORACLE_SID=xxx
sqlplus -S / as sysdba
'''
4,创建一个python subprocess模块中的Popen对象,将指定环境变量,sqlplus登录作为第一个参数传递进去:
如果只看定义的话,创建Popen对象的参数非常复杂:
class subprocess.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)
但其实一般使用用不到这么多入参,下面就是一个只有第一个必填入参的简单例子,来自于官方文档,
>>> import shlex, subprocess
>>> command_line = raw_input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print args
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # Success!
在官方文档是这样解释Popen()的第一个参数args:
在Unix上,如果args是字符串,则该字符串将被解释为要执行的程序的名称或路径。
官方文档进一步解释道:
注意如果你想要向进程的 stdin 传输数据,你需要通过 stdin=PIPE 创建此 Popen 对象。类似的,要从结果元组获取任何非 None 值,你同样需要设置 stdout=PIPE 或者 stderr=PIPE。
所以我们需要通过stdin标准输入传递具体的执行sql,并从stdout标准输出中获取执行结果,在创建Popen对象时就必须使用 stdout=PIPE, stderr=PIPE, stdin=PIPE
官方文档还指出:
参数 shell (默认为 False)指定是否使用 shell 执行程序。如果 shell 为 True,更推荐将 args 作为字符串传递而非序列。
最终,在我们的工具中,应该这样创建一个Popen对象,将上一步拼接的cmd字符串传递进去即可,后面参数使用上面推荐的值:
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE, stdin=PIPE)
完成了第四步,就建立一个打开了sqlplus 的子进程。
5,将要执行的sql输入到建立好的子进程中
比如要执行“select owner,object_name,object_type from dba_objects where status='INVALID';"查找失效对象。需要使用communicate函数去执行,并接收标准输出和错误输出:
sql = "select owner,object_name,object_type from dba_objects where status='INVALID';"
stdout, stderr = p.communicate(input=str.encode(sql))
sql的执行结果就会在stdout中了。
注意:sql在oracle中执行的报错内容也属于标准输出stdout,并不会在错误输出里。
communicate函数的功能是与进程交互:向stdin发送数据。然后从stdout读取数据,并等待进程终止。
最后会给整个Popen设置一个状态,如果是执行正常,状态为0,从p.returncode中可以获得这个值,如果是None则表示进程当前并未结束。
这样,一个简单的同步流程的python的oracle sql执行工具就完成了。下一步,可以针对特殊情况进行处理,还可以继续发掘Popen对象的功能,创建一套异步流程。




