前言 4月底的时候,写了一些shell脚本,实现了某个项目功能的自动化,虽然shell基本算是过时了,但在一些场景,它可能比Python用起来更方便。由于对shell不算很熟悉,因此在编写脚本时查询了些许资料。
现在,想将这些知识点总结下来,我选择的方式是从代码中学习,虽然读别人的代码会比较头疼,但这代码是自己写过一遍,更熟悉一些。其次读代码的方式,相比直接看定义,更偏向实践,尽管找想要的功能可能不方便,但是只需稍作修改,就可以拿去直接用了。
需求分析
从服务器下载压缩包文件
将压缩包文件解压,拿到json文件
将json文件分离成不同部分,并调用python脚本完成对数据库的查询
将查询的结果及对应的分离后的json文件回传至服务器
每天下午2点自动完成上述操作
代码分析 下载文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # !/usr/bin/sh # 1.首先是shell中定义函数的方式,直接用函数名就可以调用,不必加上括号 # function xxx () { # ... # } # xxx function download_file() { # 2.定义函数中要用到的局部变量,定义时等号两边不能有空格 ftp_user="pokemon" ftp_pass="pokemon@123456" ftp_host="ftp://65.34.87.12" subdir="Type/" # 3.执行`date -I`指令可以获得日期,形式为2022-05-20 # 通过${date//'-'/} 可以替换掉日期中的符号'-' ,得到字符串20220520,此部分可以参考文末链接 # 接下来定义一个数组,数组元素用空格分隔开 date=`date -I` date_pat=${date//'-'/} file_pat_array=("Grass" "Fire" "Water") # 4.用curl指令,获取ftp服务器指定目录下的文件内容 # -l参数,列出目录下的内容 # -s参数,静默模式,不显示传输过程和错误信息 # -u参数,指定用户:密码 files=$(curl -l -s -u ${ftp_user}:${ftp_pass} ${ftp_host}/${subdir}) # 5.遍历目录下的所有文件,和数组中定义的文件开头与当天日期进行字符串匹配, # 若发现文件名有形如"Grass???20220520???"格式的,就将其下载。 # 这里if语句比较,需要用连续两个中括号;遍历数组则用${arr[*]}的形式 for f in ${files[*]};do for file_pat in ${file_pat_array[*]};do if [[ $f == $file_pat*$date_pat* ]] then echo "start download $f ..." info=$(curl -O -u ${ftp_user}:${ftp_pass} ${ftp_host}/${subdir}/${f}) echo "finish download $f" fi done done }
解压与解密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 # Decrypt And Extract Zip Data function decrypt_gzip() { dat=".dat" json_pattern=".json" dir="/" # 1.解压压缩包文件,$1 指定了传入的第一个参数,例如`./test arg1` # 这里在循环中匹配了目录文件,.dat文件和.json文件,这么写是因 # 为刚好这个压缩包只有这三种类型文件 for line in `tar -xvzf $1`;do if [[ $line == *$json_pattern ]] then json_file=$line elif [[ $line == *$dat ]] then dat_file=$line elif [[ $line == *$dir ]] then dir=$line fi done # 2.这里使用jq命令处理json文件,安装与使用放在文末 # 然后截取字符串,去掉第一个和最后一个字符 str=$(jq '.sign' $json_file) sign=${str:1:-1} # 3.这部分是通过外部给定的解法,先解出key,再用key解开.dat包得到压缩文件 # 这里解.dat包,参数的含义需要通过man enc查看 key=$(./license-sdk-go.license-sdk-go -p AAAAA-BBBBB-CCCCC-11111-22222.lic -d $sign) openssl enc -aes-256-cbc -d -K $key -iv 0 -in $dat_file -out out.tar.gz # 4.最后解压解密后的压缩包,删除不必要的文件 tar -xvzf out.tar.gz rm out.tar.gz rm -r $dir }
文件过滤 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 # Classify Different Data Into Json function classify_data() { water_pat="Water_all" fire_pat="Fire_all" grass_pat="Grass_all" date=`date -I` date_pat=${date//'-'/} # 这部分主要逻辑都在这,过滤信息: # 1."*.gz"的操作用来遍历当前目录下的所有.gz文件 # 2.根据预先定义的pattern,过滤出对应的.gz文件 # 3.调用前面的函数decrypt_gzip进行解密与解压 # 4.将解压后目录中的.json文件移至当前目录,其余删除 # 5.通过grep提取包含特定字段的行,附加到/重定向到指定.json文件中 # 6.这里补充一个指令,wc可以用来查看文件的字节/字/行的数量 # 7.将提取出的.json文件的内容作为参数,调用其它目录下的python脚本结果保存到新的.json文件中 for f in *.gz;do if [[ $f == $water_pat* ]] then echo $f decrypt_gzip $f mv package/all/water_info.json `pwd` rm -rf package/ grep '"type": "SINGLE"' water_info.json >> single_plaintext${date_pat}.json grep '"type": "DOUBLE"' water_info.json > double_crypttext.json rm -f pokemon_info.json python3 ../query_match/match_info_map.py ./double_crypttext.json double_plaintext${date_pat}.json rm -f double_crypttext.json rm -f $f elif [[ $f == $fire_pat* ]] then echo $f decrypt_gzip $f mv package/all/fire_info.json `pwd` rm -rf package/ grep '"type": "SINGLE"' fire_info.json >> single_plaintext${date_pat}.json rm -f fire_info.json rm -f $f elif [[ $f == $grass_pat* ]] then echo $f decrypt_gzip $f mv package/all/grass_info.json `pwd`/grass_info${date_pat}.json rm -rf package/ python3 ../query_match/match_info_map.py ./grass_info${date_pat}.json grass_info_hash_map${date_pat}.json rm -f $f fi done }
文件上传 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # Upload Parsed Data function upload_data() { classify_data ftp_user="pokemon" ftp_pass="pokemon@123456" ftp_host="ftp://65.34.87.12" subdir="Result/" date=`date -I` date_pat=${date//'-'/} # 1.这里通过md5sum计算MD5值,获取到对应文件的哈希值 md5sum single_plaintext${date_pat}.json >> pokemon_md5check${date_pat} md5sum double_plaintext${date_pat}.json >> pokemon_md5check${date_pat} md5sum grass_info${date_pat}.json >> pokemon_md5check${date_pat} md5sum grass_info_hash_map${date_pat}.json >> pokemon_md5check${date_pat} # 2.将生成后的数据文件,上传服务器,并清除掉本地文件 # 3.这里上传文件需要指定参数-T,大括号内的所有文件用逗号隔开,不能有空格也不能换行! curl -u ${ftp_user}:${ftp_pass} ${ftp_host}/${subdir}/ -T \ "{single_plaintext${date_pat}.json,double_plaintext${date_pat}.json,grass_info${date_pat}.json,grass_info_hash_map${date_pat}.json,pokemon_md5check${date_pat}}" rm -rf *${date_pat}.json } # 3.最后进行函数调用,这里upload_data会调用classify_data, # classify_data内部又会调用decrypt_gzip。但是没有函数 # 调用download_file,所以需要手动调用 download_file upload_data
定时任务 1 2 3 4 5 6 7 # 最后手动设置一个定时任务 # crontab -e设定时程表,-r删除目前的时程表,-l列出目前的时程表 # f1 f2 f3 f4 f5 program # 分钟(0-59) 小时(0-23) 一个月中的第几天(1-31) 月份(1-12) 星期几(0-6) 执行的程序 # 这里设定程序,会先切到对应的目录,否则直接用绝对路径执行,内部调用其它脚本时参数可能会出错 crontab -e 0 14 * * * cd /home/pokemon_mission && ./download_decrypt_classify_upload.sh
参考链接
C编程:Shell函数详解(函数定义、函数调用)
C编程:Shell函数参数
CSDN:Linux Shell 字符串替换
博客园:CentOS7安装jq
简书:jq简易教程
wangchujiang博客:jq
Just Code:jq解析 json 实例
CSDN:Shell 数组遍历的3种方法
CSDN:curl 命令的使用:HTTP请求、下载文件、FTP上传下载