熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Oracle >> 正文

使用Expect和命名管狀遠程控制SQL*Plus

2013-11-13 22:13:00  來源: Oracle 

  在初始化一個SQL*Plus會話的時候對shell的訪問會受到HOST命令和運行存儲的SQL*Plus腳本的限制SQL*Plus不具有別名(alias)或者歷史等特性也不具備把一個命令的輸出通過管道傳入別的命令的能力如果能將SQL*Plus的特性添加到現有的shell環境中豈不是一件美事?這裡正好有一種方法可以實現這一想法
  
  在UNIX中創建一個守護進程來將命令從獨立的shell命令傳入一個SQL*Plus會話是可能實現的第一步是創建一樣能與SQL*Plus交互環境進行交互的東西雖然SQL*Plus是可交互的但是它僅限於STDOUT和STDIN所以它可以放入一個管道中
  
  sqlplus /nolog < commandssql > outputlog
  
  然而如果我們想一次發出一條SQL*Plus命令那麼就需要檢查SQL*Plus命令提示符SQL>來判斷SQL*Plus是否在等待輸入然後使用非阻塞管道這樣我們可以在遇到提示符時停止讀取數據而等待SQL*Plus更多的輸入
  
  一種天生可以完成這項工作的腳本描述語言是Expect它是Tcl/Tk程序設計語言的衍生物這個進程可以是守護進程或者是服務進程它接收單個的遠程控制命令並將它們傳遞給從屬SQL*Plus會話要與這個服務進程通信用法最簡單的協議是UNIX Domain Protocol(UDP) socketsshell可以通過UDP客戶端發出命令然後命令由服務進程接收然後其結果就通過socket發回到客戶端
  
  Expect需要一個擴展才能夠使用UDP sockets與其安裝該擴展到Expect不如在Perl中使用Expect因為Perl已經能夠很好地處理sockets在Perl歸檔中可以找到Expectpm這個程序提供在Perl中使用Expect相同的功能
  
  下面是我的簡單示例服務程序使用PerlUDP和Expectpm作為我的行程控制服務程序
  
  #!/usr/bin/perl w
  # spdpl 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($timeoutex$prompt) || die $exp>error();
  print $exp set sqlprompt $prompt;\n;
  $exp>expect($timeoutex$prompt) || die $exp>error();
  $exp>clear_accum();
  
  my $name = /tmp/sp_$ENV{USER};
  unlink($name);
  socket(SPF_UNIXSOCK_STREAM) || die socket: $!;
  bind(Ssockaddr_un($name)) || die bind: $!;
  listen(SSOMAXCONN) || die listen: $!;
  while(accept(CS))
  {
    # 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
  # spcpl
  use Socket;
  use strict;
  my $cmd = join( @ARGV);
  my $name = /tmp/sp_$ENV{USER};
  my $proto = getprotobyname(udp);
  socket(SPF_UNIXSOCK_STREAM) or die socket: $!;
  select S; $| = ; select STDOUT;
  connect(Ssockaddr_un($name)) or die connect: $!;
  print S $cmd\n;
  print while (<S>);
  close(S);
  exit;
  
  這段腳本連接到UDP服務器發送一條單行命令(所有命令行參數的串聯)然後將數據發回標准輸出最後如果服務進程還沒有運行兩個Perl腳本的前面的CSH將啟動服務程序然後將它們的參數傳給客戶端程序
  
  #!/bin/csh
  # spcsh
  set f=/tmp/sp_$user
  if (e $f == ) then
    perl spdpl &
    sleep
  endif
  perl spcpl $*
  
  現在有了這個遠程控制程序我就可以像下面這樣來做了
  
  $ alias sp spcsh
  $ 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
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.