之前在公司,在linux服务器上需要写一个shell脚本,功能如下:定时任务5秒钟执行一次,轮询当前机器(127.0.0.1)A目录,并把A目录下所有QRYTYP*开头的文件传输到另外一台机器(10.32.64.128)的B目录下,文件名也为QRYTYP*。
这样就要考虑几个问题:现在有一个文件QRYTYP123456需要传输,
1.QRYTYP123456达到A目录下,但文件过大,还在传输。而刚好被定时任务轮询到,这样B目录下的目标文件就会不完整。
2.假设QRYTYP123456已经传输完成,有一个定时任务轮询到文件,并且在FTP传输过程中,刚好又被第二个定时任务轮询到这个文件,导致一个文件并两个定时任务进行传输,不是我们希望看到的结果。
3.多任务传输大文件过程中,怎么保证FTP传输是成功的?
整个解决思路如下:
1.使用lsof指令查看文件是否被其他进程占用
2.如果文件没被占用,表示已传输完成,则把文件QRYTYP123456改成QRYTYPE123456.tmp,FTP传输的是QRYTYP123456.tmp文件到目标机器B的临时目录(TMP),传输完成后使用rename指令对文件进行重名并移到目标目录。
3.为了多个任务传输同一个文件,把每个定时任务轮询到的所有QRYTYP*文件名写到file.lst.${PID_NUMBER}文件中,PID_NUMBER = [进程号][当前时间],这样保证每个任务都处理轮询到的文件,不会造成并发传输。
4.根据ftp的传输日志,可以根据日志“226 Transfer complete”(具体日志还需根据linux的版本来定),不过一般都是已"226"开头的,所以日志里出现"226 ......",就表示该文件传输完成了。
说了那么多,上代码最实际:
整个模块的脚本都放在tools目录下,分别是:dgyc_crontab_second_5.sh,dgyc_main_for_cronb_second_5.sh,file_ftp2dgyc_for_crontab_second_5.sh,有几点要注意的:
1.dgyc_crontab_second_5.sh 这个文件的执行可通过crontab -e,添加一行:* * * * * dgyc_crontab_second_5.sh,保存退出即可。
这里是每5秒轮询一次,可以修改INTERVAL_SECOND,缩短轮询时间,最好是能被60整除,这样会比较精确。
2.dgyc_main_for_cronb_second_5.sh:LOC_SND_DIR=/si/usr/dgyc/fsend 就是你要轮询的目录,LOC_SND_TMP=/si/usr/dgyc/tmp是用来放日志的。
设置过滤规则FILE_NAME_FILTER_REGULATION="QRYTYP*",如果ls QRYTYP*这种通配符形式满足不了你的过滤需求,你可以通过管道"|",再使用 awk 或者 grep 的正则表达式来过滤也是可以的。
RMT_SND_DIR,RMT_TMP_DIR都是目标机器的目录,一般都是采用"/",因为目标机器的FTP都是根据目录来开的。
3.file_ftp2dgyc_for_crontab_second_5.sh:要修改目标机器的地址,用户名,密码。
特别提醒:
1.复制下面的代码到文本编辑器,换行符默认是"CRLF"的,但是linux是"LF"结尾,需要改过来的。
2.执行文件的过程中,看看*.sh有没有"x"权限,新手常常会犯这种错误啊!!!
#!/bin/ksh #if the script startuped by crontab, #it will ececute dgyc_main_for_cronb_second_5.sh per 5 seconeds. BASE_HOME=/tools INTERVAL_SECOND=5 let interval_count=60/INTERVAL_SECOND script_file=$BASE_HOME/dgyc_main_for_cronb_second_5.sh for((i=1;i<=${interval_count};i++));do ${script_file} 2>/dev/null & sleep ${INTERVAL_SECOND} done
#!/bin/ksh # # file directory # SCRIPT_DIR=/tools #update1 LOC_SND_DIR=/si/usr/dgyc/fsend LOC_TMP_DIR=/si/usr/dgyc/tmp RMT_SND_DIR=/ RMT_TMP_DIR=/ CURRENT_DATE_TIME=$(date "+%Y%m%d%H%M%S") PID_NUMBER=$$$CURRENT_DATE_TIME TMP_FILE_SUFFIX=".tmp" #---------------------------------------------------------------- # # send the txn file # #---------------------------------------------------------------- # # get the txn file list # cd $LOC_SND_DIR rm -f $LOC_TMP_DIR/file.lst.$PID_NUMBER > /dev/null 2>&1 ls -1 -F QRYTYP* | grep -v [/$] | grep -v ["$TMP_FILE_SUFFIX"$] | while read LINE do /usr/sbin/lsof |grep $LINE |grep -v lsof|grep -v grep > /dev/null 2>&1 if [ "$?" = "1" ] then mv $LINE $LINE$TMP_FILE_SUFFIX echo $LINE >> $LOC_TMP_DIR/file.lst.$PID_NUMBER #crontab task will send a file only. break fi done if [ ! -f "$LOC_TMP_DIR/file.lst.$PID_NUMBER" ] then exit 1 fi FILE_SIZE=`ls -l $LOC_TMP_DIR/file.lst.$PID_NUMBER | awk ‘{ print $5 }‘` if [ $FILE_SIZE -eq 0 ] then rm -f $LOC_TMP_DIR/file.lst.$PID_NUMBER > /dev/null 2>&1 fi # # get the file name and put # if [ "$FILE_SIZE" != "0" ] then cat $LOC_TMP_DIR/file.lst.$PID_NUMBER | while read LINE do #update3 /usr/sbin/lsof |grep $LINE$TMP_FILE_SUFFIX |grep -v lsof|grep -v grep > /dev/null 2>&1 if [ "$?" = "1" ] then $SCRIPT_DIR/file_ftp2dgyc_for_crontab_second_5.sh $LOC_SND_DIR $LOC_TMP_DIR $LINE $RMT_SND_DIR $RMT_TMP_DIR $PID_NUMBER $TMP_FILE_SUFFIX fi done fi if [ -f "$LOC_TMP_DIR/file.lst.$PID_NUMBER" ] then rm $LOC_TMP_DIR/file.lst.$PID_NUMBER fi #**************************************************************** # # End of file # #****************************************************************
#!/bin/ksh # # ftp constant variable # FTP_ADDR="10.32.64.128" FTP_USER=dgyc FTP_PWD=dgyc1113 # # input variable # LOC_SND_DIR=$1 LOC_TMP_DIR=$2 FILE_NAME=$3 RMT_SND_DIR=$4 RMT_TMP_DIR=$5 PID_NUMBER=$6 TMP_FILE_SUFFIX=$7 FTP_SUCCESS_MSG="^226 " FTP_INFORMATION_LOG=$LOC_TMP_DIR/"ftp_information_"$PID_NUMBER".log" # # check the file # cd $LOC_SND_DIR ls -l $FILE_NAME$TMP_FILE_SUFFIX >/dev/null 2>&1 if [ "$?" = "1" ] then exit 1 fi # # copy the orignial file to a new file # with ".tmp" in the name. # TMP_FILE_NAME=$FILE_NAME$TMP_FILE_SUFFIX # # put the file # ftp -inv $FTP_ADDR <<! >> $FTP_INFORMATION_LOG user $FTP_USER $FTP_PWD bin cd $RMT_TMP_DIR put $TMP_FILE_NAME ls -l rename $RMT_TMP_DIR/$TMP_FILE_NAME $RMT_SND_DIR/$FILE_NAME close quit ! if grep "$FTP_SUCCESS_MSG" $FTP_INFORMATION_LOG ;then #if ftp transfer success,it will remove temp file rm $TMP_FILE_NAME else #if ftp transfer failure,it will rename temp file to file mv $TMP_FILE_NAME $FILE_NAME fi if [ -f $FTP_INFORMATION_LOG ] then rm -rf $FTP_INFORMATION_LOG fi #**************************************************************** # # End of file # #****************************************************************