用 Perl DBI 实现 Oracle 导出文本

提供本人写的一个工具:Oracle unload tool,
用perl写的,可把oracle的数据表导出成文本,参数很灵活,效率也不错,虽然比不上用c写的(例如sqluldr),实测大致有sqluldr的60%左右,但也可以接受了,每秒能卸出10000至50000行不等,5-10MB/s,视实际环境而定。
优点:直接以源码发布,可移植性强,linux/unix/windows下都能用,只要配置好oracle客户端环境及perl环境(DBI+DBD::Oracle)即可。并且,稍加修改也可用于其它数据库,只要提供了DBD驱动的都行。
缺点:暂时不支持BLOB、CLOB等字段的导出,以后有时间的话再说。

源码如下(用GBK编码保存成ora_unload_gbk.pl,如果要启用UTF8,则存成ora_unload_utf8.pl,并把代码中的 my $UTF8 = 0改成 my $UTF8 =1, no utf8改成use utf8即可):
#!/usr/bin/perl

########################################################################################
#
#模块:  oracle unload tool
#
#描述:  卸载ORACLE数据库中表内容到文本文件
#运行环境要求:
  perl 5.x.x on unix/linux (http://www.perl.org/)
  or
  ActivePerl 5.x.x on windows (http://www.activestate.com/activeperl)
  perl DBI module (http://search.cpan.org/~timb/DBI/)
  perl DBD::Oracle module (http://search.cpan.org/~timb/DBD-Oracle/)
  perl Archive::Zip module (http://search.cpan.org/~timb/Archive-Zip/)
  oracle client (http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html)
#
#环境变量要求:
  1. 必须设置 ORACLE_HOME 环境变量
  2. 如果操作系统为32位LINUX, 必须设置 LD_LIBRARY_PATH=$ORACLE_HOME/lib32:$LD_LIBRARY_PATH
     如果操作系统为64位LINUX, 必须设置 LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH  
  3. 如果操作系统为AIX, 必须设置 LIBPATH=$ORACLE_HOME/lib:$LIBPATH
     (因DBD::Oracle模块只能在64位模式下成功编译)
########################################################################################

use strict;

my $version = '2.1.3';
my $devDate = '2013-04-22';
$0 = "ora_unload.pl";

my $UTF8 = 0;
no utf8;

if ( $UTF8 == 1 ) {
    # UTF8 环境设定
    binmode(STDIN,  ':encoding(utf8)');
    binmode(STDOUT, ':encoding(utf8)');
    binmode(STDERR, ':encoding(utf8)');
    $ENV{'NLS_LANG'} = "SIMPLIFIED CHINESE_CHINA.AL32UTF8";
} else {
    $ENV{'NLS_LANG'} = "SIMPLIFIED CHINESE_CHINA.ZHS16GBK";
}

# 命令行参数
use Getopt::Long qw(:config no_ignore_case);
my $opt_db_user;
my $opt_db_password;
my $opt_db_instance=$ENV{'ORACLE_SID'};
my $opt_connect_url;
my $opt_table_name;
my $opt_table_rows_limit = -1;
my $opt_sql_file;
my $opt_sql_string;
my $opt_output_file = "unload.out";
my $opt_delimiter_str = "|";
my $opt_dos_endline;
my $opt_date_format = 'YYYY-MM-DD';
my $opt_time_format = 'HH24:MI:SS.FF';
my $opt_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF';
my $opt_quiet_mode;
my $opt_enclosed_str;
my $opt_want_trim_char;
my $opt_want_format_number;
my $opt_want_head;
my $opt_want_bzip2;
my $opt_want_gzip;
my $opt_want_zip;
my $opt_want_help;
my $opt_batch_size=10000;

¬ify if $#ARGV == -1;

GetOptions(
           'user=s'             =>  $opt_db_user,
           'password=s'         =>  $opt_db_password,
           'instance=s'         =>  $opt_db_instance,
           'connecturl=s'       =>  $opt_connect_url,
           'table=s'            =>  $opt_table_name,
           'rows=i'             =>  $opt_table_rows_limit,
           'filequery=s'        =>  $opt_sql_file,
           'stringquery=s'      =>  $opt_sql_string,
           'output=s'           =>  $opt_output_file,
           'delimiter=s'        =>  $opt_delimiter_str,
           'DateFormat=s'       =>  $opt_date_format,
           'TimeFormat=s'       =>  $opt_time_format,
           'TimestampFormat=s'  =>  $opt_timestamp_format,
           'MSDOS'              =>  $opt_dos_endline,
           'TrimChar'           =>  $opt_want_trim_char,
           'FormatNumber'       =>  $opt_want_format_number,
           'Head'               =>  $opt_want_head,
           'Enclosed=s'         =>  $opt_enclosed_str,
           'Bzip2'              =>  $opt_want_bzip2,
           'Gzip'               =>  $opt_want_gzip,
           'Zip'                =>  $opt_want_zip,
           'BatchSize=i'        =>  $opt_batch_size,
           'quiet'              =>  $opt_quiet_mode,
           'help|?'             =>  $opt_want_help
          )
   or ¬ify;

&usage() if defined $opt_want_help;
¬ify("$0: 未设置任何数据库连接属性") unless ( ($opt_db_user && $opt_db_password) || $opt_connect_url );
¬ify("$0: 只能选择一种数据库连接方式") if ( $opt_db_user && $opt_db_password && $opt_connect_url );

¬ify("$0: input_sql_file, query_string, table 不能同时设置") if ( $opt_sql_file && $opt_sql_string && &opt_table_name );
¬ify("$0: input_sql_file 和 query_string 不能同时设置") if ( $opt_sql_file && $opt_sql_string );
¬ify("$0: table 和 input_sql_file 不能同时设置") if ( $opt_sql_file && $opt_table_name );
¬ify("$0: table 和 query_string 不能同时设置") if ( $opt_sql_string && $opt_table_name );
¬ify("$0: table 或 input_sql_file 或 query_string 必须设置一项") unless ( $opt_table_name || $opt_sql_file || $opt_sql_string ) ;

if($opt_batch_size <=0 ) {
    $opt_batch_size = 10000;
}

$opt_delimiter_str = parse_special_char($opt_delimiter_str);

my $endLine = " ";
if (defined $opt_dos_endline) {
    $endLine = " ";
}

# 快速模式
my $fast_mode = 1;
if ($opt_want_trim_char || $opt_want_format_number || $opt_enclosed_str) {
    $fast_mode = 0;
}

my $notify_lines = $opt_batch_size;

###############  处理过程 #############

# 生成SQL
my $SQLString;
if($opt_sql_string) {
    $SQLString = $opt_sql_string;
} elsif($opt_sql_file) {
    die "$opt_sql_file 不存在或不是一个文件! " if(! -f $opt_sql_file);
    die "$opt_sql_file 不可读! " if( ! -r $opt_sql_file);
    open(SQLFILE, "<$opt_sql_file");
    while() {
        $SQLString=$SQLString.$_;
    }
    close(SQLFILE);
} else {
   $SQLString = "SELECT * FROM $opt_table_name";
   if($opt_table_rows_limit > 0) {
       $SQLString .= " WHERE rownum <= $opt_table_rows_limit";
   }
}

#如果SQL以分号结束,则去掉它
$SQLString = trim($SQLString);
$SQLString =~ s/;+$//;

# 对SQL进行安全性检查
&checksql($SQLString);

# 输出文件预处理
die "输出文件: $opt_output_file 是一个目录! " if(-d $opt_output_file);
if(-f $opt_output_file) {
   if(-w $opt_output_file) {
     unlink "$opt_output_file";
   } else {
     die "$opt_output_file 无权写入! ";
   }
   

# 连接数据库
use DBI;
use DBD::Oracle;
if ($opt_connect_url) {
   $opt_db_instance = (split('@',$opt_connect_url))[1];
   ($opt_db_user, $opt_db_password)  = split('/', (split('@',$opt_connect_url))[0]);
   #print $opt_db_instance, ' ', $opt_db_user, ' ',$opt_db_password;
}
#my $connectUrl = $opt_connect_url;
#if ($connectUrl eq "") {
   $connectUrl = $opt_db_user . '/' . $opt_db_password . '@' . $opt_db_instance;
#}
print "连接数据库 ... " unless defined $opt_quiet_mode;
#my $dbh = DBI->connect("dbi:Oracle:$opt_db_instance" , $opt_db_user, $opt_db_password , {ora_array_chunk_size => $opt_batch_size}) || die "连接数据库失败! ";
my $dbh = DBI->connect("dbi:Oracle:$opt_db_instance" , $opt_db_user, $opt_db_password ) || die "连接数据库失败! ";

# 设置日期、时间字段格式
my $alterSql="alter session set nls_date_format='$opt_date_format'";
$dbh->do($alterSql);
$alterSql="alter session set nls_time_format='$opt_time_format'";
$dbh->do($alterSql);
$alterSql="alter session set nls_time_tz_format='$opt_time_format'";
$dbh->do($alterSql);
$alterSql="alter session set nls_timestamp_format='$opt_timestamp_format'";
$dbh->do($alterSql);
$alterSql="alter session set nls_timestamp_tz_format='$opt_timestamp_format'";
$dbh->do($alterSql);

# 执行SQL查询
print "执行查询SQL语句 ... " unless defined $opt_quiet_mode;
my $sth;
$dbh->{RowCacheSize} = $opt_batch_size;
#$sth = $dbh->prepare($SQLString) || die "SQL预处理失败: ".$dbh->errstr;
$sth = $dbh->prepare($SQLString,{ora_prefetch_rows=>$opt_batch_size}) || die "SQL预处理失败: ".$dbh->errstr;
#$sth = $dbh->prepare($SQLString,{ora_prefetch_memory=>16777216 * 2}) || die "SQL预处理失败: ".$dbh->errstr;
$sth->execute || die "执行SQL失败: ".$sth->errstr;

# 取得字段类型
my $col;
my @fieldNames;        # 字段名数组
my @fieldTypes;        # 字段类型数组
my @fieldTypeNames;    # 字段类型名称数组
my @fieldPrecisions;   # 字段精度数组
my @fieldScales;       # 字段小数有效位数组

for($col=0; $col < $sth->{NUM_OF_FIELDS} ; $col++ ) {
   $fieldNames[$col] = $sth->{NAME}->[$col];
   $fieldTypes[$col] = $sth->{TYPE}->[$col];
   $fieldTypeNames[$col] = $dbh->type_info($fieldTypes[$col])->{TYPE_NAME};
   $fieldPrecisions[$col] = $sth->{PRECISION}->[$col];
   $fieldScales[$col] = $sth->{SCALE}->[$col];
   #print $fieldNames[$col].":".$fieldTypes[$col].":".$fieldTypeNames[$col].":".$fieldPrecisions[$col].":".$fieldScales[$col]." ";
}

# 卸数
print "开始卸出数据 ... " unless defined $opt_quiet_mode;
use Time::HiRes qw/time/;
my $start_time = time();

open(OUTFILE,">$opt_output_file") or die "打开 $opt_output_file 失败! ";

if ( $UTF8 == 1 ) {
    # UTF8 环境设定
    binmode(OUTFILE, ':encoding(utf8)');
}

$" = $opt_delimiter_str; #字段分隔符

# 输出标题行
if($opt_want_head) {
    print OUTFILE "@fieldNames", "$endLine";
}

my $row_count = 0;
my $rows = []; # cache for batches of rows
#my $fetch_size = $opt_batch_size % 2 == 0 ? $opt_batch_size / 2 : ($opt_batch_size + 1) / 2;
my $fetch_size = $opt_batch_size;
# 快速模式循环
if($fast_mode == 1) {
   while(
          my $row_ref = ( shift(@$rows) || # get row from cache, or reload cache:
                          shift(@{$rows=$sth->fetchall_arrayref(undef,$fetch_size)||[]}) )
        ) {
       print  OUTFILE "@$row_ref", "$endLine";
       $row_count ++;

       if(!$opt_quiet_mode && ($row_count % $notify_lines == 0)) {
            print "$row_count ";
       }
   } #while
} #快速模式循环
else { #普通模式循环
   #生成格式化字串
   my $fmt_str;
   my @char_cols;

   $col = 0;
   foreach(@fieldTypes) {
       my $field_fmt;
       if(defined $opt_want_format_number && $fieldTypes[$col] == 3 && $fieldScales[$col] > 0) { # ORACLE 的NUMBER类型
             $field_fmt = "%0." . $fieldScales[$col] . "f"; #浮点数格式化
       } else {
             $field_fmt = "%s";
             if(defined $opt_want_trim_char && $fieldTypes[$col] == 1) { #CHAR
                 push @char_cols, $col;
             }
       }

       if($opt_enclosed_str) {
           $field_fmt = $opt_enclosed_str . $field_fmt . $opt_enclosed_str;
           

       $fmt_str .= $field_fmt;

       if($col < $#fieldTypes) {
          $fmt_str .= $";
       }

       $col ++;
   }

   $fmt_str .= $endLine;
   #print $fmt_str, " ";
  
   #对 CHAR 去空格
   if(@char_cols) {
       #print "trim ";
       while(
              my $row_ref = ( shift(@$rows) || # get row from cache, or reload cache:
                              shift(@{$rows=$sth->fetchall_arrayref(undef,$fetch_size)||[]}) )
            ) {

           foreach(@char_cols) {
               $$row_ref[$_] =~ s/s+$//g;
           }

           #输出
           printf OUTFILE $fmt_str, @$row_ref;
           $row_count ++;
 
           if(!$opt_quiet_mode && ($row_count % $notify_lines == 0)) {
               print "$row_count ";
           }
  
       } #while
    } else { #不去空格
       #print "notrim ";
       while(
              my $row_ref = ( shift(@$rows) || # get row from cache, or reload cache:
                              shift(@{$rows=$sth->fetchall_arrayref(undef,$fetch_size)||[]}) )
            ) {

           #输出
           printf OUTFILE $fmt_str, @$row_ref;
           $row_count ++;
 
           if(!$opt_quiet_mode && ($row_count % $notify_lines == 0) ) {
               print "$row_count ";
           }
  
       } #while

    } #else

} #普通模式循环
if($row_count % $notify_lines != 0) {
   print "$row_count " unless defined $opt_quiet_mode;
}
$sth->finish();
$dbh->disconnect();
close(OUTFILE);

if($row_count == 0) {
  print "无数据 " unless defined $opt_quiet_mode;
  #unlink $opt_output_file;
  exit 0;
} else {
  my $end_time = time();
print $start_time, ",", $end_time , " ";
  my $time_cost = sprintf("%.5f", $end_time - $start_time);
  my $file_size = sprintf("%.3f", (-s $opt_output_file) / 1024 / 1024);
  my $avg_unload =  $time_cost > 0 ? sprintf("%.f", $row_count / $time_cost)  : 0;
  my $avg_size = $time_cost > 0 ? sprintf("%.3f", $file_size / $time_cost)  : 0;

  unless( defined $opt_quiet_mode) {
      print "*" x 60 , " ",
            "输出文件: $opt_output_file ",
            "共计卸出: ${row_count}行 ${file_size}MB ",
            "共计耗时: ${time_cost}秒 ${time_cost}秒 ",
            "平均速度: ${avg_unload}行/秒 ${avg_size}MB/秒 ",
            "*" x 60 , " ";
  }
}

#压缩数据文件
if ( defined $opt_want_bzip2 || defined $opt_want_gzip || defined $opt_want_zip ) {
    print "压缩文件 ... " unless defined $opt_quiet_mode;
      
    # Bzip2
    if ( defined $opt_want_bzip2 ) {
       use IO::Compress::Bzip2 qw(bzip2 $Bzip2Error) ;
       print "压缩: $opt_output_file --> $opt_output_file.bz2 ... " unless defined $opt_quiet_mode;
       bzip2 $opt_output_file  => "$opt_output_file" . ".bz2" or die "bzip2 失败: $Bzip2Error ";
       print "成功! " unless defined $opt_quiet_mode;
    }
   
    # Gzip
    if ( defined $opt_want_gzip ) {
       use IO::Compress::Gzip qw(gzip $GzipError) ;
       print "压缩: $opt_output_file --> $opt_output_file.gz ... " unless defined $opt_quiet_mode;
       gzip $opt_output_file  => "$opt_output_file" . ".gz" or die "gzip 失败: $GzipError ";
       print "成功! " unless defined $opt_quiet_mode;
    }
   
    # Zip
    if ( defined $opt_want_zip ) {
       use Archive::Zip qw( :ERROR_CODES );
       my $zip=Archive::Zip->new();
       print "压缩: $opt_output_file --> $opt_output_file.zip ... " unless defined $opt_quiet_mode;
       $zip->addFile("$opt_output_file");
       die "zip 失败! " if $zip->writeToFileNamed("$opt_output_file.zip") != AZ_OK;
       print "成功! " unless defined $opt_quiet_mode;
    }

    print "压缩文件完毕 ... " unless defined $opt_quiet_mode;
    unlink $opt_output_file;
}

print "卸数完毕! " unless defined $opt_quiet_mode;

exit 0;

################  用户定义函数 #################
sub trim {
  my $string = shift;
  $string =~ s/^s+//;
  $string =~ s/s+$//;
  return $string;
}

sub ltrim {
  my $string = shift;
  $string =~ s/^s+//;
  return $string;
}

sub rtrim {
  my $string = shift;
  $string =~ s/s+$//;
  return $string;
}

sub checksql {
  my $buf = shift;
  $buf = trim($buf);
  die "$0: 只允许使用 SELECT 语句 " if ( ! $buf =~ m/^SELECT/i );
}

sub notify {
  warn "@_ " if @_;
  die  "用法: $0 {-u username -p password [-i db_instance] | -c connecturl} {-f sql_file | -s sql_string | -t table} [可选参数] ",
       "提示: $0 --help (可以得到更详细的用法) ";
}

sub parse_special_char {
    $_ = shift;
    my $i;
    my $ret;

    # "x77" -> 0x77
    if (/^(\[xX][0-7][0-9a-fA-F])+$/) { #16进制
       s/\/0/g;
    }

    if (/^(\[01][0-7][0-7])+$/) { #8进制
       s/\/0/g;
    }

    if (/^(0[xX][0-7][0-9a-fA-F])+$/) { #16进制
       my $hex_count = length($_) / 4;
       $i = 0;
       while ($i<$hex_count) {
           $ret .= chr(hex(substr($_, $i * 4, 4)));
           $i++;
       }
    elsif (/^(0[01][0-7][0-7])+$/) { #8进制
       my $oct_count = length($_) / 4;
       $i = 0;
       while ($i<$oct_count) {
           $ret .= chr(oct(substr($_, $i * 4, 4)));
           $i++;
       }
    } elsif(/^(\[nrtfbae])+/) {
       my $chr_count = length($_) / 2;
       $i = 0;
       while ($i<$chr_count) {
           my $chr_str = substr($_, $i * 2, 2);
           if ($chr_str eq ' ') {
              $ret .= " ";
           } elsif ($chr_str eq ' ') {
              $ret .= " ";
           } elsif ($chr_str eq ' ') {
              $ret .= " ";
           } elsif ($chr_str eq 'f') {
              $ret .= "f";
           } elsif ($chr_str eq '') {
              $ret .= "";
           } elsif ($chr_str eq 'a') {
              $ret .= "a";
           } else {
              $ret .= "e";
           }
           $i++;
       }
    } else {
       $ret = $_;
    }
    return $ret;
}

sub usage {
  die " ",
      "程序描述: Oracle unload tool ( Oracle 数据库卸数工具 ) ",
      "版    本: $version ",
      "日    期: $devDate ",
      "作    者: 薛文龙 ",
      "邮    箱: flippy@sina.com ",
      " ",
      "用法: $0 {-u username -p password [-i db_instance] | -c connecturl} {-f sql_file | -s sql_string | -t table} [可选参数] ",
      " ",
      "必填参数: ",
      --user=username 或 -u username            数据库用户名 ",
      --password=password 或 -p password        数据库密码 ",
      --instance=db_instance 或 -i db_instance  数据库实例名 (默认: 环境变量 ORACLE_SID) ",
         或 ",
      --connecturl=url 或 -c url                数据库连接字符串 (例如: 'scott/tiger@orcl') ",
      (注:以上两种方式任选其一) ",
      " ",
      --stringquery=string 或 -s string         用于查询的 SQL 语句 (例如: 'select * from dual') ",
         或 ",
      --filequery=file 或 -f file               用于查询的 SQL 文件名 (例如: '/tmp/test.sql') ",
         或 ",
      --table=table_name 或 -t table_names      要卸数的表名 ",
      (注:以上三种方式任选其一) ",
      " ",
      "可选参数: ",
      --output=output_file 或 -o output_file    输出卸数文件 (默认: unload.out) ",
      --delimiter_string='str' 或 -d 'str'      输出字段分隔字符串 (默认: '|') ",
      (注:特殊字符可用16进制、8进制或转义字符:0x00-0x7f 0000-0177 f  a e)'," ",
      " ",
      --rows=n 或 -r n                          与 -t 选项同时使用, 限定卸出的最大记录数为 n, 未设置 -t 则无效 (默认: 不限制) ",
      " ",
      --DateFormat=dateFormat                   日期字段格式 (默认: 'YYYY-MM-DD') ",
      --TimeFormat=timeFormat                   时间字段格式 (默认: 'HH24:MI:SS.FF') ",
      --TimestampFormat=timestampFormat         时间戳字段格式 (默认: 'YYYY-MM-DD HH24:MI:SS.FF') ",
      --TrimChar                                对 CHAR 型字段去除空格 (默认: 不去除空格) ",
      --FormatNumber                            对 NUMBER 型字段进行格式化,  例如: NUMBER(10,2) 的值 0 => 0.00 (默认: 不进行格式化) ",
      --Enclosed='str'                          字段采用 str 包围 (默认:否) ",
      --Head 或 -H                              输出文件第一行打印字段名称 (默认: 否) ",
      --MSDOS 或 -M                             使用 DOS 格式的行结束符: '\r\n' (默认: '\n') ",
      " ",
      --Bzip2 或 -B                             对卸数文件进行 bzip2 压缩 (默认: 不压缩) ",
      --Gzip 或 -G                              对卸数文件进行 gzip 压缩 (默认: 不压缩) ",
      --Zip 或 -Z                               对卸数文件进行 zip 压缩 (默认: 不压缩) ",
      " ",
      --BatchSize=n                             缓冲区大小, 必须>0 (默认值:10000) ",
      --quiet 或 -q                             采用安静模式, 不打印正常提示信息 (错误信息除外) ",
      --help 或 -h 或 -?                        打印帮助信息 ",
      " ",
      "实例: ",
      $0 -u scott -p tiger -i orcl -t emp -o emp.txt ",
      $0 -c scott/tiger@orcl -s "select * from emp" -o emp.txt -d "$|$" ",
      $0 -c scott/tiger@orcl -f cust_query.sql -o cust_query.txt --DateFormat='MM/DD/YYYY' --Gzip ";
}

另外再提供一个简单加密的版本,可用于生产环境,懂perl的话要解密也很容易:
#!/usr/bin/perl

# function: do_prg
# run a program using a encoded body
sub do_prg {
    my ($prg, $prg_body, @prg_args) = @_;

    use MIME::Base64 qw(decode_base64);   
    use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
    use File::Temp qw(tempfile tmpnam);

    # decode
    my ($tfh1, $tfn1) = tempfile();
    print $tfh1 decode_base64($prg_body);
    close($tfh1);

    # gunzip
    my $tfn2 = tmpnam();
    gunzip $tfn1 => $tfn2 or die "gunzip failed: $GunzipError ";
    unlink $tfn1;

    # run
    system $prg, $tfn2, @prg_args;
    my $result = ( $? == 0 ) ? 0 : -1;
    unlink $tfn2;
    return $result;
}

###################  main program start #########################
die "ORACLE_HOME not set! " unless defined $ENV{'ORACLE_HOME'};

# for Aix
# set $ORACLE_HOME/lib as the first LIBPATH
# for Aix 64bit / Oracle 10g 64bit / perl 64bit
##my $oracle_libpath = $ENV{'ORACLE_HOME'} . "/lib:";
##unless ( $ENV{'LIBPATH'} =~ /A$oracle_libpath/ ) {
##   $ENV{'LIBPATH'} = $oracle_libpath . $ENV{'LIBPATH'};
##}

# for linux
# set $ORACLE_HOME/lib as the first LD_LIBRARY_PATH
# for Linux 64bit / Oracle 10g 64bit / perl 64bit
my $oracle_libpath = $ENV{'ORACLE_HOME'} . "/lib:";
unless ( $ENV{'LD_LIBRARY_PATH'} =~ /A$oracle_libpath/ ) {
   $ENV{'LD_LIBRARY_PATH'} = $oracle_libpath . $ENV{'LD_LIBRARY_PATH'};
}

# set LOCALE
$ENV{'LC_ALL'} = "en_US";
$ENV{'LANG'} = "en_US";

# perl locate
my $perl = "/usr/bin/perl";
die "$perl is not exists! " unless -x $perl;

# child program encoded body, Don't Modify this!!!
my $prg_encoded = '
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
';

my $ret = do_prg($perl, $prg_encoded, @ARGV);
exit $ret;


请使用浏览器的分享功能分享到微信等