在初始化一個SQL*Plus會話的時候
對shell的訪問會受到HOST命令和運行存儲的SQL*Plus腳本的限制
SQL*Plus不具有別名(alias)或者歷史等特性
也不具備把一個命令的輸出通過管道傳入別的命令的能力
如果能將SQL*Plus的特性添加到現有的shell環境中豈不是一件美事?這裡正好有一種方法可以實現這一想法
在UNIX中
創建一個守護進程來將命令從獨立的shell命令傳入一個SQL*Plus會話是可能實現的
第一步是創建一樣能與SQL*Plus交互環境進行交互的東西
雖然SQL*Plus是可交互的
但是它僅限於STDOUT和STDIN
所以它可以放入一個管道中
sqlplus /nolog < commands
sql > output
log
然而
如果我們想一次發出一條SQL*Plus命令
那麼就需要檢查SQL*Plus命令提示符
SQL>
來判斷SQL*Plus是否在等待輸入
然後使用非阻塞管道
這樣我們可以在遇到提示符時停止讀取數據而等待SQL*Plus更多的輸入
一種天生可以完成這項工作的腳本描述語言是Expect
它是Tcl/Tk程序設計語言的衍生物
這個進程可以是守護進程或者是服務進程
它接收單個的遠程控制命令並將它們傳遞給從屬SQL*Plus會話
要與這個服務進程通信
用法最簡單的協議是UNIX Domain Protocol(UDP) sockets
shell可以通過UDP客戶端發出命令
然後命令由服務進程接收
然後其結果就通過socket發回到客戶端
Expect需要一個擴展才能夠使用UDP sockets
與其安裝該擴展到Expect
不如在Perl中使用Expect
因為Perl已經能夠很好地處理sockets
在Perl歸檔中可以找到Expect
pm
這個程序提供在Perl中使用Expect相同的功能
下面是我的簡單示例服務程序
使用Perl
UDP和Expect
pm作為我的行程控制服務程序
#!/usr/bin/perl
w
# spd
pl
the SQL*Plus daemon server
use strict;
use Expect;
use Socket;
# set up expect
#
timeout after about
minutes
my $timeout =
;
#
scan for the SQL*Plus prompt
my $prompt =
SQL>
;
my $exp = new Expect();
$exp
>raw_pty(
);
$exp
>log_stdout(
);
$exp
>spawn(
sqlplus
/nolog
) || die
unable to spawn sqlplus: $!
;
$exp
>expect($timeout
ex
$prompt) || die $exp
>error();
print $exp
set sqlprompt $prompt;\n
;
$exp
>expect($timeout
ex
$prompt) || die $exp
>error();
$exp
>clear_accum();
my $name =
/tmp/sp_$ENV{USER}
;
unlink($name);
socket(S
PF_UNIX
SOCK_STREAM
) || die
socket: $!
;
bind(S
sockaddr_un($name)) || die
bind: $!
;
listen(S
SOMAXCONN) || die
listen: $!
;
while(accept(C
S))
{
# single threaded to avoid confusion
my $cmd = <C>;
$cmd =~ s/[\r\n]*$//g;
print $exp $cmd
\n
;
if ($cmd =~ /^exit$/mi)
{
print C
exit
\n
;
close C;
last;
}
$exp
>expect($timeout
$prompt) || die $exp
>error();
print C $exp
>before();
close C;
}
$exp
>soft_close();
close S;
unlink($name);
exit;
我把管道名叫做
sp_{username}
這樣就允許同一個用戶的兩個不同會話共享同一個SQL*Plus會話
我使用默認的
SQL>
提示符
但是為了避免
SQL>
顯示我的數據並導致數據被截斷的可能性
最好還是使用一個長的隨機字符串
下一步是Perl客戶端
#!/usr/bin/perl
w
# spc
pl
use Socket;
use strict;
my $cmd = join(
@ARGV);
my $name =
/tmp/sp_$ENV{USER}
;
my $proto = getprotobyname(
udp
);
socket(S
PF_UNIX
SOCK_STREAM
) or die
socket: $!
;
select S; $| =
; select STDOUT;
connect(S
sockaddr_un($name)) or die
connect: $!
;
print S $cmd
\n
;
print while (<S>);
close(S);
exit;
這段腳本連接到UDP服務器
發送一條單行命令(所有命令行參數的串聯)
然後將數據發回標准輸出
最後
如果服務進程還沒有運行
兩個Perl腳本的前面的CSH將啟動服務程序然後將它們的參數傳給客戶端程序
#!/bin/csh
# sp
csh
set f=/tmp/sp_$user
if (
e $f ==
) then
perl spd
pl &
sleep
endif
perl spc
pl
$*
現在
有了這個遠程控制程序
我就可以像下面這樣來做了
$ alias sp sp
csh
$ sp
connect scott/tiger
Connected
$ sp
select * from dual;
D
X
$ sp
select sysdate from dual;
| grep MAY
MAY
$ sp
select * from emp;
| wc
$ sp
exit
最後一條命令將關閉服務程序
這個提示中的腳本非常粗糙
不允許使用多行SQL語句
如果一個SQL語句不完整
服務進程將會掛起等待SQL提示符
如果SQL提示符改變服務進程也會掛起
但是在使用SQL*Plus的時候不丟掉shell環境依然非常有用
對於一般的命令使用它可以創建別名和使用shell功能
使用歷史和替代變量
或者在內嵌環境中使用SQL*Plus
From:http://tw.wingwit.com/Article/program/Oracle/201311/18504.html