这个脚本是我第一次接触shell写的脚本,当时是公司提的一个小需求,我记得写这个脚本的时候我是以一个实习生的身份刚进公司两三天。当时的我查了将近一个星期的百度还是没能找到一个符合我这个需求的脚本。那个时候才发现原来有些问题并不是直接百度便能找到符合自己需求的源码,直接ctrl C 、ctrl V 就解决了,最后也是公司大佬们一点一滴的指导才勉强写完了这个脚本。现在马上实习快结束了,所以想写播客记录下自己的实习生活。
实现功能:定时的去遍历自己监听的目录,当出现文件时,判断其完整性,如果文件完整便将其上传到指定的ftp目录上,如果文件不完整则先不处理,等到下次遍历到的时候其完整再处理,现阶段只支持上传文件,如果监听目录有子目录的情况,脚本可能会报错(暂时不支持)。
一些实现的方法:
1. 定时的遍历:我这里选用的是linux 服务器自带的crontab 定时任务管理器。
2. 完整性判断:因为文件的真实来源我们并不知道,所以无法获取文件最初的md5值,所以这里我用了比较“直板”的方法,使用了文件的最后修改时间去判断,如果文件属于一个上传状态,那个其最后修改时间一定是会刷新的。
3. 每次上传文件都会去尝试连接ftp,不会出现当ftp突然挂了,遍历到监听文件完整后去执行上传操作,最后删除监听目录的文件,导致文件丢失。
shell代码实现:脚本名称(upload.sh)
#!/bin/bash ## 用于将文件上传到ftp ##连接ftp的函数 function ftpConnectTest(){ ftp -n -v <<EOF open ${1} ${2} user ${3} ${4} bye EOF } ##判断文件夹存不存在 function ftpCheckDir(){ ftp -n -v <<EOF open ${1} ${2} user ${3} ${4} cd ${5} bye EOF } ## 将指定目录的文件上传到ftp function upload_file_ftp() { src=$1 ip=$2 port=$3 username=$4 password=$5 target=$6 connect_record=/var/log/synchronismfile_log/ftpconnect.log mkdir_record=/var/log/synchronismfile_log/mkdirrecord.log upload_record=/var/log/synchronismfile_log/fileupload.log ##遍历源目录 for file in `ls ${src}` do #判断遍历到的是不是文件夹 if [ -d $1"/"$file ]; then cat /dev/null > ${mkdir_record} ftpCheckDir $ip $port $username $password $target"/"$file >> ${mkdir_record} #判断是否登录成功 if [ `grep -c 'Login successful.' ${mkdir_record}` -eq 0 ]; then if [ `grep -c 'User logged in, proceed.' ${mkdir_record}` -eq 0 ]; then echo "连接ftp服务器失败,请查看是否网络原因!">>/var/log/synchronismfile_log/syncfileerror.log exit 1 fi fi ##连接出现两种情况,一个是ftp 一个是sftp if [ `grep -c 'Failed to change directory.' ${mkdir_record}` -eq 1 ]; then { ftp -v -n <<EOF open $ip $port user $username $password cd $target mkdir $file bye EOF } elif [ `grep -c 'No such directory.' ${mkdir_record}` -eq 1 ]; then { ftp -v -n <<EOF open $ip $port user $username $password cd $target mkdir $file bye EOF } else echo "ftp目录存在!" fi #继续递归遍历 upload_file_ftp $src"/"$file $ip $port $username $password $target"/"$file fi #如果遍历到的是文件,文件完整则上传,文件不完整则不上传 ##上传最后修改时间在10秒前的文件 if [ -f $src"/"$file ]; then ##ftp连接测试挂掉了我直接给你退出了 cat /dev/null > ${connect_record} ftpConnectTest $ip $port $username $password >> ${connect_record} if [ `grep -c 'Login successful.' ${connect_record}` -eq 0 ]; then if [ `grep -c 'User logged in, proceed.' ${connect_record}` -eq 0 ]; then echo "连接ftp服务器失败,请查看是否网络原因!">>/var/log/synchronismfile_log/syncfileerror.log exit 1 fi fi last_update_time=`stat -c %Y $1"/"$file` current_time=`date +%s` echo "当前时间:"$current_time echo "文件最后修改时间: "$last_update_time echo "两者时间之差:"$[ $current_time - $last_update_time ] filename1=$(basename "$file") ttt=${filename1: -5} if [ $ttt != _temp ]; then if [ $[ $current_time - $last_update_time ] -gt 3 ]; then filename=$(basename "$file") echo "文件名:"$filename ##判断文件名是否存在文件上传目录中 if [ `grep -c $filename $upload_record` -eq 0 ]; then echo "文件上传记录中没有"$filename"文件将要开始上传" echo $filename >> $upload_record ftp -v -n <<EOF open $ip $port user $username $password lcd $src cd $target binary hash prompt put $file bye EOF rm -rf $src"/"$file sed -i "/$filename/d" /var/log/synchronismfile_log/fileupload.log fi else echo "文件不完整" fi fi fi done } ##主函数 echo "upload.sh文件上传脚本执行" starttime=`date +'%Y-%m-%d %H:%M:%S'` ##被监听目录 src=$1 ##目标目录 bak=$2 ##对传入的ftp进行截取 userandpwd=${bak#*//} userpwd=${userandpwd%@*} user=${userpwd%:*} pwd=${userpwd#*:} urlandport=${userandpwd##*@} ip=${urlandport%:*} portandsrc=${urlandport#*:} port=${portandsrc%%/*} target=/${portandsrc#*/} ##在/var/log 目录下创建 synchronismfile_log目录,用于存放各种日志 if [ ! -d "/var/log/synchronismfile_log" ]; then `mkdir /var/log/synchronismfile_log` fi if [ ! -d $src ]; then echo "本地源目录不存在,请先创建源目录">>/var/log/synchronismfile_log/syncfileerror.log exit 1 fi ##创建记录文件上传名字的日志 if [ ! -f "/var/log/synchronismfile_log/fileupload.log" ]; then `touch /var/log/synchronismfile_log/fileupload.log` fi ##用于记录日志 connect_record=/var/log/synchronismfile_log/ftpconnect.log cat /dev/null > ${connect_record} ##ftp连接测试 ftpConnectTest $ip $port $user $pwd >> ${connect_record} if [ `grep -c 'Login successful.' ${connect_record}` -eq 0 ]; then if [ `grep -c 'User logged in, proceed.' ${connect_record}` -eq 0 ]; then echo "连接ftp服务器失败,请查看是否网络原因!">>/var/log/synchronismfile_log/syncfileerror.log exit 1 fi fi ##调用文件上传函数 upload_file_ftp $src $ip $port $user $pwd $target endtime=`date +'%Y-%m-%d %H:%M:%S'` start_seconds=$(date --date="$starttime" +%s); end_seconds=$(date --date="$endtime" +%s); echo "脚本执行结束,运行时间: "$((end_seconds-start_seconds))"s"
脚本调用方法:
上传到ftp: ./upload.sh /home/test ftp://ftptest:123456@ip:port/testB
上传到本地另一个目录:./upload.sh /home/test /home/testB
参数说明:/home/test :本地要监听的文件目录(脚本运行前得先建立)
ftp://用户名:密码@ip:port/testB:testB 上传到ftp的目录(也需先存在)
/home/testB: 要将脚本拷贝到的本地另一个目录(目录需先存在)
说明:如果要上传的文件过于珍贵,需自己先备份,脚本并不能保证百分百将文件不造成文件丢失
当然如果各位小伙伴,还有更好更安全的实现方式,欢迎大家来讨论
PS:如果有需要实时监听并上传的,大家可以尝试下inotify-tools工具去监控。这个我也写过一版,不过可能是我基础不行,还是哪里的逻辑出了问题,在监听上传时会导致服务器很卡,如果大家能找到这个的解决方案,也希望能告诉我下。