【IT168 专稿】在Linux/Unix下一些程序是命令行或者服务程序,一般都有一个配置文件来描述其运行规则,而这些规则通常会保存在数据库中,由其它应用程序来维护。这就牵涉一个问题,如何从数据库中获取最新的规则来驱动后台程序。同样后台程序运行结束后,生成的结果有时需要写回数据库,以便其它应用程序读取。
通过阅读本文章,读者能了解Shell脚本和Informix交互的一些常用技巧和方法,实现诸如查询、增加、删除、修改数据功能,最终实现数据库、后台程序之间的信息交互。
1. Informix简要介绍
Informix是IBM公司出品的关系数据库管理系统(RDBMS)家族。作为一个集成解决方案,它被定位为作为IBM在线事务处理(OLTP)旗舰级数据服务系统。 IBM对Informix和DB2都有长远的规划,两个数据库产品互相吸取对方的技术优势。目前最新版本的是Informix 11.7,这个最新的版本在未来十年对IBM Informix继续提供力量,其引擎有效的解决OLTP,决定了可以更好的支持各种规模的企业和合作伙伴的决策支持应用。
2.Linux/Unix Shell简要介绍
Linux/Unix Shell也叫做命令行界面,是一种控制系统的脚本语言。它是Linux/Unix操作系统下传统的用户和计算机的交互界面。用户直接输入命令来执行各种各样的任务,隐藏了操作系统低层的细节。
Shell的主要功能:命令执行、输入/输出重定向、环境控制、后台处理、Shell脚本。
3.Shell与Informix交互的基本方法
Shell通过调用dbaccess在Informix上执行各种SQL,而通过调用dbload可以将文件加载到数据库中。
3.1. 通过dbaccess连接数据库
dbaccess提供了用于输入、执行和调试结构化查询语言(SQL)语句与存储过程语言(SPL)例程的用户界面。假设联机的数据库服务器包含一个名为mystores的数据库,要使mystores数据库成为当前数据库,可以通过db2access mystores来启动DB-Access.也可以通过命令dbaccess //xyz/newstores 连接到xyz数据库服务器上的newstores数据库(未联机)。
3.2. 通过dbaccess执行SQL
当从命令行调用dbaccess时,可以指定某个数据库作为当前的数据库、执行包含一个或多个SQL语句的文件以及显示多个级别的错误信息。
例1:dbaccess test insert.sql
表示:在test数据库执行名为insert.sql的文件中SQL语句的命令。
例2:dbaccess - insert.sql
表示:在insert.sql上文件中指定的数据库上执行文件中的SQL语句的命令。
下面函数被调用后,调用dbaccess来执行相关的SQL并将结果输出到标准输出中。
清单1
{
sqltxt="$1";
pre_sql="output to pipe cat without headings ";
sqltxt=$pre_sql"$sqltxt";
echo "$sqltxt"|dbaccess test 2>/dev/null;
}
在清单1的代码中,$1 表示函数的第1个参数,在这里第1个参数为要执行的SQL语句;pre_sql变量中的代码是用来屏蔽dbaccess执行时输出的运行日志信息,如果不加此设置,不会影响函数执行结果,只是执行过程中出现很多dbaccess运行日志信息。
echo "$sqltxt"|dbaccess test 2>/dev/null表示用dbaccess命令连接test数据库,然后执行$sqltxt语句,并将错误输出到/dev/null。
上面函数在Shell中调用方式如下:
3.3. 利用Shell调用dbaccess导出记录
下面函数被调用后,会执行传入的SQL并将结果导出到一个文件中。
清单2
{
sqltxt="$1";
unload_file="$2";
temp_file="$3";
pre_sql="unload to $unload_file delimiter ',' ";
post_sql="";
echo $pre_sql > $temp_file;
echo "$sqltxt" >> $temp_file;
echo $post_sql >> $temp_file;
dbaccess testdb < $temp_file 2>/dev/null;
rm -f $temp_file;
}
在清单2的代码中,$1 、$2 、$3表示函数的第1个、第2个、第3个参数。
第1个参数为SQL语句。
第2个参数为输出文件名称。
第3个参数为临时文件名称(执行结束会被删除)。
unload to $unload_file delimiter ',' 表示记录导出时存为文件名为$unload_file的文件,在这里记录之间的分隔符用逗号表示。
$temp_file文件为临时文件,用来保存SQL语句,在函数执行末尾其内容将被删除。
dbaccess testdb < $temp_file 2>/dev/null表示用dbaccess命令连接testdb数据库,然后执行$temp_file文件中的SQL语句,并将错误输出到/dev/null。
上面函数在Shell中调用方式如下:
ExecuteSqlUnloadFile "select c1,c2 from t_test" "t_test.dat" "t_test.tmp";
3.4. 利用dbload导入文件
dbload的功能是将一至多个ASCII文件中的数据传送到一至多个已有的表中。
语法为:dbload [-d dbname ] [-c cfilename] [-l logfile] [-e errnum] [-n nnum] [-i inum] [-s] [-p] [-r] [-k] [-X]。
其中主要选项说明如下:
-d dbname 指定接收数据的数据库名
-c cfilename 指定指定dbload命令文件的文件名
-l logfile 指定错误日志文件的文件名
-r 在装入数据期间允许其他用户修改表中数据(装入数据期间不封锁表)
-s 指示dbload 检查命令文件中语句的语法,而不插入数据
-i inum 指示dbload忽略指定数目的行数
-n nnum 指示dbload在插入指定数目的新行后提交操作
-e errnum 指定dbload 中止前可读入的坏行数
-p 如果坏行数超过了极限,给出提示信息,请用户指定解决方法
例如: dbload -d testdb -c load.ctl -n 10000 -l error.log。这个命令表示将按照load.ctl控制文件的指示,加载数据到testdb 数据库中, 每10000条记录提交一次,错误数据将写入error.log文件。以下是load.ctl控制文件的内容。
清单3
INSERT INTO data_crm_list;
清单3中的内容表示将crm.dat数据文件加载到data_crm_list表中,分隔符是'|', 数据字段个数为3。
${PageNumber}4. Shell与Informix交互实例
通过第三章的介绍,读者已经基本了解Shell与Informix的交互方式。在本章中,作者将通过一个具体实例展现Shell与Informix交互过程。
4.1. 场景概述
脚本名称:test.sh
演示步骤:
A:先在Informix中创建2个表,分别为t_test和t_test2;
t_test表结构:
(
user_code VARCHAR(20),
user_name VARCHAR(20),
user_desc VARCHAR(50)
)
t_test2表结构:
(
user_code VARCHAR(20),
user_name VARCHAR(20),
user_desc VARCHAR(50)
)
B:在t_test中插入2条记录;
insert into t_test values('1002','guess','this is a test')
C:在Linux终端下执行test.sh脚本。
运行结果:
A:脚本会读取t_test表中的2条记录,并导出为文件t_test.dat;
B:t_test.dat文件被导入t_test2表中,此时t_test2记录数和t_test一样。
4.2. 完整代码和注释
DBTOOL="dbaccess"
DBCONNT="test"
#功能说明:执行SQL并将结果导出到一个文件中
#参数说明:
# 第1个参数为SQL语句
# 第2个参数为输出文件名称
# 第3个参数为临时文件名称(执行结束会被删除)
ExecuteSqlUnloadFile()
{
sqltxt="$1";
unload_file="$2";
temp_file="$3";
pre_sql="SET LOCK MODE TO WAIT 10 ;unload to $unload_file delimiter ',' ";
post_sql="";
echo $pre_sql > $temp_file;
echo "$sqltxt" >> $temp_file;
echo $post_sql >> $temp_file;
$DBTOOL $DBCONNT < $temp_file 2>/dev/null;
rm -f $temp_file;
}
#功能说明:执行SQL
#参数说明:
# 第1个参数为SQL语句
ExecuteSqlSelect()
{
sqltxt="$1";
pre_sql="SET LOCK MODE TO WAIT 10 ;output to pipe cat without headings ";
sqltxt=$pre_sql"$sqltxt";
echo "$sqltxt"|$DBTOOL $DBCONNT 2>/dev/null;
}
#功能说明:去掉变量的前后空白
trim()
{
echo $1 |sed 's/^ \+//'|sed 's/ \+$//'
}
#功能说明:得到t_test指定记录中某个字段的值
get_username_bycode()
{
user_code="$1";
EXECSQL="select user_name
from t_test
where user_code="$user_code";";
echo $EXECSQL
user_name=`ExecuteSqlSelect "$EXECSQL"`;
user_name=`trim $user_name`;
echo "user_name:["$user_name"]";
}
#功能说明:得到t_test中所有记录,并导出为文件t_test.dat
get_all_username()
{
EXECSQL="select user_code,user_name,user_desc
from t_test;";
echo $EXECSQL
ExecuteSqlUnloadFile "$EXECSQL" "t_test.dat" "username.tmp";
}
#功能说明:生成入库配置文件
#参数说明:
# 第1个参数为需入库的表名称
# 第2个参数为待入库的文件名称
# 第3个参数为文件分隔符
# 第4个参数为文件列个数
make_loader()
{
table_name=$1
data_name=$2
sep=$3
tab_num=$4
echo "file \"$data_name\" delimiter \"$sep\" $tab_num;" > $CTL_FILE;
echo "insert into $table_name;" >> $CTL_FILE;
}
#功能说明:按照入库配置要求,将数据导入表中
#参数说明:
# 第1个参数为bad文件
# 第2个参数为log文件
start_loader()
{
badfile_name=$1;
logfile_name=$2;
#这里的10000是指多少条数据commit一次,可以根据实际情况调节大小。
dbload -d $DBCONNT -c "$CTL_FILE" -l "$badfile_name" -n 10000;
}
run()
{
#得到用户编号为1001的用户名称
get_username_bycode "1001";
#得到t_test表中用户编号、用户名称、用户描述信息,并保存到t_test.dat文件中
get_all_username;
#通过awk将t_test.dat最后一列转换为大写字母
`cat t_test.dat | awk -F',' -v OFS=',' '{print $1,$2,toupper($3)}' > t_test2.dat`
CTL_FILE="./t_test2.ctl"
#生成入库配置
make_loader "t_test2" "./t_test2.dat" "," "3";
#执行入库
start_loader "./t_test2.bad" "./t_test2.log";
}
#--------------------主程序--------------------
run;
4.3. 运行结果与分析
总计 4
-rwxr-xr-x 1 testtest 2683 05-04 14:37 test.sh
test @ dwserver : /home/test /sgy $ ./test.sh --------执行test.sh
select user_name from t_test where user_code=1001;----49行代码输出
user_name:[test] ----52行代码输出
select user_code,user_name,user_desc from t_test; ----59行代码输出
DBLOAD Load Utility INFORMIX-SQL Version 10.00.UC5 ---DBLOAD运行输出
Copyright IBM Corporation 1996, 2004 All rights reserved
Software Serial Number AAA#B000000
Table t_test2 had 2 row(s) loaded into it.
test @ dwserver : /home/test /sgy $ ls -l
总计 16
-rwxr-xr-x 1 testdba 2683 05-04 14:37 test.sh
-rw-r--r-- 1 test dba 0 05-04 14:38 t_test2.bad
-rw-r--r-- 1 test dba 59 05-04 14:38 t_test2.ctl
-rw-r--r-- 1 test dba 51 05-04 14:38 t_test2.dat
-rw-r--r-- 1 test dba 53 05-04 14:38 t_test.dat
test @ dwserver : /home/test /sgy $ head t_test2.ctl ----入库配置文件
file "./t_test2.dat" delimiter "," 3;
insert into t_test2;
test @ dwserver : /home/test /sgy $ head t_test.dat ----导出的t_test记录
1001,test,this is a test
1002,guess,this is a test
test @ dwserver : /home/test /sgy $ head t_test2.dat ----经过99行awk处理后的记录
1001,test,THIS IS A TEST
1002,guess,THIS IS A TEST
运行结束后,t_test2表中多出2条记录,且user_desc字段的值为大写。
结束语
本文主要介绍了Linux/Unix下Shell如何实现Informix数据库的交互。文章首先对Informix和Shell进行了简单介绍,然后通过介绍dbaccess和dbload两个应用程序的用法来使读者对Shell和Informix交互有一个基本认识。文章最后以一个完整的实例来展示Shell和Informix的各种交互,使读者加深理解,在例子中会用到awk、sed来对数据格式化,这些工具的加入使得Shell处理很多应用时更加强大,所以在实际各种应用中,Shell会结合awk、sed等工具来实现对数据库中导出的信息格式化,或者在文件导入数据库之前对文件中记录进行格式化。
作者介绍
张晓丹,IBM CDL软件工程师, 具有丰富的自动化测试经验、RQM应用经验、BI项目测试经验、以及Web application开发经验。
程平平,IBM CDL 软件工程师,主要从事 Optim Query Tuner 产品开发工作,在Eclipse插件开发有一定的经验。
王苹, IBM CDL 软件工程师,主要从事 DB2 for z/OS产品功能测试工作,在数据库应用和自动化测试工具开发方面有一定的经验。