posts - 187,comments - 176,trackbacks - 0

在编写shell脚本的时候,有时候需要在执行完shell脚本(手工执行或利用crontab定时执行),需要服务器上制定目录下的文件或子目录进行删除操作,以免下次执行时产生数据冲突。

现需要编写一个shell脚本,完成以下功能:
[1]从系统B那边将指定目录下的文本文件FTP到系统A本地服务器的制定目录下。
[2]FTP到本地后,执行系统A本地上的shell脚本,将文本文件中的信息入到系统A本地映射的数据库中。
[3]入库完毕后,再FTP到系统B将远程的0指定目录下的那个文本文件删除。
[4]系统A上shell将其本地的相关目录下的文文本件和子目录的内容删除,防止下次执行产生数据重复和冲突。
注:系统A和B都是HP-Unix,且系统A上的shell_a和系统B上的shell_b执行时间为每天晚上23:00之后,它们时间差大概十分钟(如 在23:00时刻执行shell_b,到23:10时刻执行shell_a)。

上面步骤当中涉及到删除操作的有[3]和[4],而问题就发生在第[4]步。第[4]步中的删除系统A本地上目录下的文件,这里我是用rm来进行删除的。首先我的shell脚本部署在系统A的:/home/coner/blackall/目录下,名称为black_all.sh。
脚本中对于删除本地文件的代码如下(注:blackall目录下的data 和 ctl子目录已经存在)

#!/bin/sh
sqlldr_dir=`pwd`
sqlldr_data_dir
="data"

cd 
$sqlldr_dir/$sqlldr_data_dir
rm 
-*.txt

对于上面代码,当shell脚本手工执行的时候,结果没有任何问题,sqlldr_dir的值此时为脚本所在的路径:/home/coner/blackall/,故cd $sqlldr_dir/$sqlldr_data_dir可以成功登录到/home/coner/blackall/data下,再进行rm -f *将data子目录下的所有文件删除(文件不分前缀和后缀名)。但对于定时任务,要用crontab部署的情况下呢?
问题就出在这里,笔者测试,利用crontab将该shell部署后,设置定时时间,到点后通过重定向生成的日志中,看到sqlldr_dir打印到日志中的值并不是/home/coner/blackall/,而是/home/coner。故可以推断到,上面的删除代码,当用定时任务部署的时候,$sqlldr_dir/$sqlldr_data_dir的值就为/home/coner/data,而在系统A上/home/coner目录下没有此目录,即cd 操作失败,系统抛出异常(及时碰巧有这个目录data,也是误删除文件的操作)。接着执行下面的rm -f *。此时,crontab命令对于该shell脚本所在位置的解析为/home/coner,故在执行rm -f *的时候就将/home/coner目录下的所有文件全部删除掉了,这样就造成误删除文件的恶果。

郁闷+揪心中...不过幸运的是,执行脚本前有做手工的文件备份。

鉴于上面情况,笔者将shell脚本重新进行了审核,将sqlldr_dir=`pwd`换成sqlldr_dir=/home/coner/blackall,并对cd操作加上异常保护。代码如下:

#!/bin/sh
sqlldr_dir=/home/coner/blackall
sqlldr_data_dir
="data"

cd 
$sqlldr_dir/$sqlldr_data_dir >/dev/null 2>&1
if [ $? -ne 0 ]
     then 
          echo 
"the path of $sqlldr_dir/$sqlldr_data_dir invalid.."
          
exit  
     
else
          echo 
"begin rm -f $sqlldr_dir/$sqlldr_data_dir/*.txt"
          rm 
-*.txt
fi

或者在能确定路径的情况下直接写成定值: rm -f /home/coner/blackall/data/*.txt


对于crontab命令执行定时任务,和登录系统后,在用户coner下手工执行shell是有区别的。前者在执行脚本时候,解析pwd命令只能解析到当前用户即coner目录为止,即上面的sqlldr_dir的值为/home/coner,而在用户coner下手工执行shell是在解析pwd命令时可以正常解析到/home/coner/blackall。这个和shell脚本初始化.profile(Unix)或.bash_profile(Linux)没有关系,即使在脚本中进行下面的初始化操作:

# if HP-UX and AIX
if [ -/home/zxin10/.profile ]
 then
          echo 
"INIT profile TO UNIX"
          
. /home/zxin10/.profile
fi
# if Linux
if [ -/home/zxin10/.bash_profile ]
           then
          echo 
"INIT bash_profile TO LINUX"
           
. /home/zxin10/.bash_profile
fi

在用crontab执行脚本时,解释pwd的时候,sqlldr_dir的值还是/home/coner。
上面的初始化脚本,主要是针对利用crontab部署定时任务时,因为crontab启动的命令并不读当前的.profile,因此所有的程序需要的环境变量需要用程序或shell自己去设置(和手工执行shell不同,手工执行shell是在某个登录用户下执行的,登录该用户之后,就已经初始化了环境变量配置文件),那么在shell中调用第三方软件的一些命令(如Oracle的sqlplus和sqlldr等命令)时,如果程序中直接写成sqlplus或sqlldr命令来调用时,脚本是识别不到这些命令的。在定时任务执行完毕后的系统邮件(mail命令查看)中会提示找不到该命令。解决方法就是在执行shell脚本的业务逻辑前先初始化操作系统的环境变量配置文件.profile或.bash_profile。如果不初始化也可以,那么在shell中调用这些命令时,需要将其写成绝对路径,如:/home/oracle/oracle92/bin/sqlldr这样的形式。

上面是我个人的实践和理解,欢迎大家提出好的建议,共同参考学习。



posted on 2008-05-18 17:18 cheng 阅读(2939) 评论(0)  编辑  收藏 所属分类: Unix/Linux

只有注册用户登录后才能发表评论。


网站导航: