最近在写一个shell脚本,跑在suse下面,定时执行,在编写和调试过程中遇到一些问题,在这里小结一下。
操作系统:Suse for linux 服务器:tomcat
功能:当用户在门户页面上传用户号码文件来完成为这些号码批量订购某些cp发布的产品(如铃音盒大礼包等等)。
下面是门户上传页面的主要代码:
<%@ page import="com.zte.framework.common.*,
com.zte.framework.menu.*,
com.zte.framework.util.*,
java.util.*,
java.io.*,
com.zte.omm.*,
java.text.SimpleDateFormat,
org.apache.commons.fileupload.*"%>
<%@ include file="../common/commonHeader.jsp" %>
<%@ include file="../common/commonJspParam.jsp" %>
<c:set var = "basename" value = "resources.CRBT.user_res" scope="request" />
<html>
<body>
<%
String localPath;
String ordertype = "";
String productindex = "";
String productid = "";
String contentindex = "";
String contentid = "";
String backurl = "";
String presentmonth = "";

if (ConfigImpl.IS_OS_WINDOWS)
{
localPath = "c:/zxin10/was/tomcat/webapps/opportal/CRBT/";
}

else
{
localPath = "/home/zxin10/was/tomcat/webapps/opportal/CRBT/";
}
String localfilename = "";//((String)session.getId()).substring(0, 10);
//检查输入请求是否为multipart表单数据。
boolean isMultipart = FileUpload.isMultipartContent(request);

if (isMultipart)
{
//为该请求创建一个DiskFileUpload对象,通过它来解析请求。执行解析后,所有的表单项目都保存在一个List中。
DiskFileUpload upload = new DiskFileUpload();
List items = upload.parseRequest(request);
Iterator itr = items.iterator();

while (itr.hasNext())
{
FileItem item = (FileItem) itr.next();
//检查当前项目是普通表单项目还是上传文件。

if (item.isFormField())
{
System.out.println("item.getFieldName(): = "+item.getFieldName());

if ("order_type".equals(item.getFieldName()))
{
ordertype = item.getString();

if ("1".equals(ordertype) || "2".equals(ordertype) || "3".equals(ordertype))
{
localPath = localPath + "userimportsub/";
backurl = "/CRBT/user/user_import_order.jsp";
}

else if ("4".equals(ordertype))
{
localPath = localPath + "userimportunsub/";
backurl = "/CRBT/user/user_import_unorder.jsp";
}

else if ("5".equals(ordertype) || "6".equals(ordertype))
{
localPath = localPath + "userimportsend/";
backurl = "/CRBT/user/user_import_send.jsp";
}
File file = new File(localPath);

if (!file.exists())
{
file.mkdirs();
}
}

else if ("productindex".equals(item.getFieldName()))
{
productindex = item.getString();
}

else if ("productid".equals(item.getFieldName()))
{
productid = item.getString();
}

else if ("contentindex".equals(item.getFieldName()))
{
contentindex = item.getString();
}

else if ("contentid".equals(item.getFieldName()))
{
contentid = item.getString();
}

else if ("presentmonth".equals(item.getFieldName()))
{
presentmonth = item.getString();
}
}

else
{//如果是上传文件,显示文件名。

if(item.getName() != null)
{ //获得文件名,包括路径

if (item.getSize() > 170 * 1024)
{
%>
<c:set var="info_msg_type" value="1" scope="request"/>
<c:set var="info_msg" value="user.file.toolong" scope="request"/>
<jsp:forward page="<%= page_msg + backurl %>"/>
<%
}
localfilename = localfilename + new SimpleDateFormat("MMddHHmmss").format(new Date());
File savedFile = new File(localPath, localfilename);
item.write(savedFile);
System.out.println("localfilename = "+localPath+localfilename);
Process process = Runtime.getRuntime().exec("chmod 777 "+localPath+localfilename);
break;
}
}
}
%>
该jsp运行于tomcat容器中,由于开发规范要求tomcat部署在root用户下,所以当门户上传执行上面代码并在服务器上的形如localPath = "/home/zxin10/was/tomcat/webapps/opportal/CRBT/"路径下生成0621131603文件时,其被写权限只针对root用户,而我开发的shell脚本是运行在zxin10用户下,所以在jsp代码上传文件的代码中需要添加:
Process process = Runtime.getRuntime().exec("chmod 777 "+localPath+localfilename); 来将该文件权限全部放开,让zxin10用户也可以对其进行读写执行。
号码文件usercode.txt在windows xp本地(保存方式为ascii,保存方式为utf-8时,则第一行号码前多出FFFE符号)用编辑器在hex模式下打开时为:
13111111..
13122222..
13133333..
当通过jsp在服务器端生成usercode.txt文件后,用ftp工具连上服务器并打开文件,在hex模式下得到:
13111111..
13122222..
13133333..
其中..分别对应于\r和\n(十六进制为0D0A),注意:在用ftp工具以asc方式上传号码文件后,其hex模式的每行号码后面只有\n(十六进制为0A),这和jsp代码上传是有区别的,如果以二进制方式上传,则结果和jsp代码上传后一致。
同时,对于windows本地的号码文件,由于是人工从其他系统导出的,对于最后一行的号码后面的回车换行存在不确定性,就是说,有可能是
情况[1]
13111111..
13122222..
13133333
或
情况[2]
13111111..
13122222..
13133333..
或
情况[3]
13111111..
13122222..
13133333..
..
..
现在需要实现的功能是:shell脚本对其进行读行处理的同时需要从数据库中查询出另外2个字段(号码类型 记录号)并将这2个字段追加在usercode.txt文件中每一行的号码后面,形如:
13111111 号码类型 记录号..
13122222 号码类型 记录号..
13133333 号码类型 记录号..
如果按照下面的shell实现方式:
cat $sh_uploadfile | while read tmp
do
echo "$sh_usercode|$sh_usertype|$sh_recordid" >>$sh_uploadfile
done


其中$sh_uploadfile的值为文件usercode.txt在服务器端的绝对路径,如:/home/zxin10/was/tomcat/webapps/opportal/CRBT/**//**

当shell脚本开始读取服务器端的文件时,问题出现了
对于[1]:
13111111..
13122222..
13133333
这种情况,当cat $sh_uploadfile | while read tmp的时候,其读取到的行数为2行,而不是3行,即执行cat $sh_uploadfile | wc -l
的结果为2;
对于[3]
13111111..
13122222..
13133333..
..
..
这种情况,当cat $sh_uploadfile | while read tmp的时候,其读取到的行数为5行,而不是3行,即执行cat $sh_uploadfile | wc -l
的结果为5;
这样的不确定性在执行下面的shell脚本时,带来了严重的数据错误,对于【1】,则第3行号码没有被执行到;对于【2】,则会出现有多余的2行数据。对于这种情况,就需要在shell脚本读取$sh_uploadfile 文件的时候,对其进行先进行换行回车处理,将其统一转换成:
13111111.
13122222.
13133333.
格式,其中.为\n(十六进制为0A,换行)这样shell读取该文件的行数才和号码行数一致(该文件每行结尾为..,n也可以,shell脚本读取的行数也是和号码文件一致,只是人为的习惯于文件在linux上回车换行为\n罢了)。
下面这段是shell脚本处理usercode.txt号码文件中缺少换行或多出换行符的主要逻辑:
#########################################################
################# 处理号码类型 ##########################
#########################################################
cat $sh_uploadfile|awk '{sub(/\r$/,"");print}'>hex.txt
num=`cat hex.txt | wc -l`
echo $num

#########################################################
#处理hex.txt
#########################################################
cat /dev/null >$sh_uploadfile
cat hex.txt | while read tmp
do
sh_usercode=`echo $tmp | awk -F "|" '{ print $1 }'`
echo $sh_usercode
echo "



ha




.."
send_day=`$db_conn_cmd_sqlplus<<!
set pagesize 0
set linesize 32767
set feedback off
select decode(phn.phonetype,1,0,2,1,3,2),func_sp_getmaxvalue('crbt_recordid', 1)
from zxdbm_ismp.ssys_phone phn
where rpad($sh_usercode, 15, '0') > rpad(phn.startprefix, 15, '0') and rpad($sh_usercode, 15, '0') < rpad(phn.endprefix, 15, '9')
/
!`
#########################################################
#号码类型
#########################################################
sh_usertype=`echo $send_day|awk '{ print $1 }'`
if [ "$sh_usertype" == "" ]
then
#号码在号段表中没有对应的号码类型不存在,则sh_usertype填999
sh_usertype=999
fi
echo $sh_usertype
#########################################################
#文件中的每个号码唯一个记录号
#########################################################
sh_recordid=`echo $send_day|awk '{ print $2 }'`
if [ "$sh_recordid" == "" ]
then
#号码在号段表中没有对应的号码类型不存在,记录号也不存在。则sh_recordid的值再调一次func的sequence获得记录号。
send_day=`$db_conn_cmd_sqlplus<<!
set pagesize 0
set linesize 32767
set feedback off
select func_sp_getmaxvalue('crbt_recordid', 1) from dual
/
!`
sh_recordid=`echo $send_day|awk '{ print $1 }'`
fi
echo $sh_recordid
echo "




..hi




.."
#########################################################
#在上传的号码文件中追加号码类型和记录号
#########################################################
#用户号码|号码类型|记录号
echo "$sh_usercode|$sh_usertype|$sh_recordid" >>$sh_uploadfile
done
#删除hex文件
echo "start delete file hex.txt"
rm -f hex.txt
echo "delete end"
#########################################################
其中:cat $sh_uploadfile|awk '{sub(/\r$/,"");print}'>hex.txt 执行时,将 $sh_uploadfile按要求处理为中间文件hex.txt,然后读取hex.txt,并将从数据库中取出另为2个字段(号码类型和记录号)追加并将最后的组合结果重新写到$sh_uploadfile文件中,同时将hex.txt文件删除,这样无论用户上传的文件中最后一个号码结尾是否有回车换行符,还是最后认为有多的回车换行符,在shell处理过程中全部过滤掉了,保证了号码文件被追加字段后的行数正确性,以及追加记录准确性。