从oracle 10g版本起,添加了schedule job功能,该功能支持调用操作系统命令
所以我们可以做个这样的尝试,
将要执行的操作系统命令通过utl_file包生成一个.bat文件,然后通过schedule job来执行这个bat文件,
举个例子,如果我们想要定时重启windows上的时间时间同步服务,在cmd下是执行
net stop w32time
net start w32time
那么我们可以把这两条命令,写成一个time_r.bat文件,
V_File := Utl_File.Fopen(I_db_Path,I_file_name ,'W');
Utl_File.Put_Line(V_File, l_cmd_text);
Utl_File.Fclose(V_File);
保存在D:\TEMP\time_r.bat,然后创建一个不自动执行的临时schedule job,
begin
sys.dbms_scheduler.create_job(job_name => 'SYS.TIME_R',
job_type => 'EXECUTABLE',
job_action => 'c:\windows\system32\cmd.exe /c "time_r.bat"',
start_date => to_date('02-07-2017 00:00:00', 'dd-mm-yyyy hh24:mi:ss'),
repeat_interval => 'Freq=Hourly;Interval=4;ByMinute=50;BySecond=0',
end_date => to_date(null),
job_class => 'DEFAULT_JOB_CLASS',
enabled => false,
auto_drop => false');
end;
/
再执行这个scheduler JOB,
begin
dbms_scheduler.run_job('SYS.TIME_R');
end;
执行完后删除这个job和bat文件(删job也可通过“auto_drop”参数控制,但不方便调试排查故障)。
UTL_FILE.FREMOVE(I_db_Path, I_file_name );
dbms_scheduler.drop_job(I_job_name);
为了方便灵活使用,我们可以把中间步骤全部进行抽象,封装成一个存储过程,实际使用时无需去管job和bat文件。比如下面这个样子
declare
l_cmd_text VARCHAR2(4000);
begin
l_cmd_text:='net stop w32time
net start w32time';
TEXT_TO_CMD_P(l_cmd_text);
end;
但是实际使用中可能会遇到几个问题
1.由于utl_file操作文件时,实际上操作的是在数据库内映射的文件夹,所以一般建议再人工指定一个文件夹参数;
2.有时候比较危险的操作,想只生成bat文件但不执行;
3.有时候会同时几个会话在执行不同操作系统命令,如果bat文件重复了,可能会导致异常,如果文件名完全随机,在排查故障的时候又方便找到自己需要的bat文件。
所以,可以再加几个参数
CREATE OR REPLACE PROCEDURE TEXT_TO_CMD_P(I_db_Path IN VARCHAR2, --数据库映射目录
I_cmd_text IN VARCHAR2,--要执行的操作系统命令
I_file_name IN VARCHAR2,--bat文件名前缀
I_run in varchar2 --是否执行bat文件
)
完整源码请从我的github上下载
https://github.com/Dark-Athena/text_to_cmd-oracle
目前该过程暂只支持windows,理论上linux也是可以支持的,但我手上暂时无linux的环境,以后会考虑更新一版自适应操作系统的。
注意:
1.命令中不能存在用户交互式操作,且命令执行完后必须确保操作系统的进程会退出,或者强制在bat文件末尾再补个exit,否则会出现程序一直在执行的情况
2.windows环境中,ORACLE数据库大多是用的system用户,该用户几乎是windows的最高权限用户,所以执行操作系统命令要慎重。
3.由于是在oracle数据库执行的对应的操作系统用户下创建的操作系统进程,所以其他的操作系统用户是无法看到对应的操作命令执行窗口的
4.建议在存储过程正式执行前,确保生成的bat文件内容的正确性
另外,还可以通过调用java再调用操作系统命令,但是授权会比较麻烦,网上可以搜索到比较完整的资料,这里就不赘述了。