原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。
Shell批量处理
工作中,会一点点简单的Shell脚本编程,对于部分场景,能提高效率不少,比如这样一种需求:
业务同学需要去年一整年的订单量,所幸,线上已经有一个接口能够根据时间范围查询订单量,可惜的是,由于数据量过大,这个接口只能支持1天的时间范围。怎么办,为这个临时小需求再实现一个查询接口?
如果你会Shell脚本,就很简单了,将1年拆成365天,每一天,查询一下接口获取订单数量,最后再将数量汇总,即是1年的订单量。
下面主要是一些Shell脚本代码,但这里并不会介绍Shell的基础语法,没有Shell基础的同学,先看看这里一篇文章让你彻底掌握 shell 语言
nc模拟实现查询订单数量接口
这里为了方便,使用ncat命令模拟实现这个简单的http接口,接口实现的代码如下:
#!/bin/bash
while read line;do
if [[ "$line" =~ ^GET && "$line" =~ countOrder ]];then
#获取请求参数
start_time=$(grep -oP "(?<=start_time=)[\dT:-]+" <<<$line);
end_time=$(grep -oP "(?<=end_time=)[\dT:-]+" <<<$line);
#生成随机的单量
num=$(head /dev/urandom | tr -dc 0-9 | head -c 5);
length=$(echo -n "$num"|wc -c)
printf "%s,%s,%s\n" $start_time $end_time $num > /dev/tty;
#返回http响应
printf "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: %d\r\n\r\n%s" $length $num
fi;
done
接口接收到请求后,生成随机的订单量返回。
保存脚本为countOrderNum.sh,然后启动这个接口服务,如下:
# 启动接口服务
ncat -lk 8080 -e countOrderNum.sh
# curl调用接口
curl -s http://localhost:8080/countOrder?start_time=2019-01-01T00:00:00\&end_time=2019-01-02T00:00:00
效果如下:
![301b840080f883d0a5a1d47d8f17d9dc.png](https://img-blog.csdnimg.cn/img_convert/301b840080f883d0a5a1d47d8f17d9dc.png)
查询每日单量
接口已经有了,接下来实现查询每一天订单数量的脚本,如下:
#!/bin/bash
let begin=$(date -d "2019-01-01" +%s);
for i in {0..364}; do
#计算本次循环的起始结束时间
let start=$(($begin+$i*(24*60*60)));
let end=$(($begin+($i+1)*(24*60*60)));
#时间缀转日期字符串
start_time=$(date --date="@$start" +"%Y-%m-%dT%H:%M:%S");
end_time=$(date --date="@$end" +"%Y-%m-%dT%H:%M:%S");
#调用接口获取数量
count=$(curl -s http://localhost:8080/countOrder?start_time=$start_time\&end_time=$end_time);
printf "%s\t%s\t%s\n" $start_time $end_time $count;
done
保存脚本为getDayCount.sh,脚本执行逻辑如下:
- 脚本以2019-01-01为起始时间,计算出相应的unix时间缀,保存在begin变量中。
- 接下来循环365次,其中i取值为0,1,2...364,计算每一天的start、end,其中
24*60*60
即1天的秒数,然后将start,end转换为日期字符串start_time,end_time,第一次循环start_time,end_time为2019-01-01T00:00:00,2019-01-02T00:00:00,第二次循环为2019-01-02T00:00:00,2019-01-03T00:00:00。 - 使用curl调用接口,并将start_time,end_time参数赋值。
- 打印出每日单量。
执行脚本,执行结果保存在data.txt中,效果如下:bash getDayCount.sh | tee data.txt
![9466e2deb55ec3ce458b8424fd8986e5.png](https://img-blog.csdnimg.cn/img_convert/9466e2deb55ec3ce458b8424fd8986e5.png)
汇总单量
然后将data.txt中的每日单量汇总后,即是一年总单量,使用awk命令很容易实现,将第3列累加起来即可。
$ cat data.txt|awk '{a += $3} END{print a}'
2480856
pv查看进度
365天,会执行365次接口查询,整体还是比较慢的,那么执行过程中,如何查看脚本执行的进度呢?
通过在执行脚本时,添加pv命令即可,如下:
$ bash getDayCount.sh | pv -l -s 365 > data.txt
264 0:00:37 [6.98 /s] [========================================> ] 72% ETA 0:00:14
如上,用pv来观测进度是比较方便的,pv实现进度原理如下:
- 前面getDayCount.sh脚本每查询了一天就会输出一条日志,总共会输出365条日志
- pv的-l参数,表示统计从输入流读取到的行数,而-s 365参数,指定总共会读取到的行数,那么每当之前的getDayCount.sh完成1天的查询,就会输出一条记录给pv,pv就会将自己读到的行数加1,然后除以365来计算进度。
- pv显示中,最前面的264表示已处理数据量,这里即代表目前已查询了264天的数据,0:00:37代表脚本执行已花费的时间,6.98/s代表每秒处理查询6.98个,即处理速度。
xargs并发查询
上面的脚本,去查询每日单量,是顺序查的,查完第1天,再查第2天,一直到结束,整个查询下来,花费的时间也不少。
我们可以利用xargs来并发处理,脚本需要拆分为两个,如下:
- 生成1年的时间范围
#!/bin/bash
let begin=$(date -d "2019-01-01" +%s);
for i in {0..365}; do
let start=$(($begin+$i*(24*60*60)));
let end=$(($begin+($i+1)*(24*60*60)));
start_time=$(date --date="@$start" +"%Y-%m-%dT%H:%M:%S");
end_time=$(date --date="@$end" +"%Y-%m-%dT%H:%M:%S");
printf "%s\t%s\n" $start_time $end_time;
done
保存为genDay.sh,与之前的逻辑类似,只是这个脚本单纯的生成时间范围,不调用接口。
2. 执行查询
#!/bin/bash
count=$(curl -s http://localhost:8080/countOrder?start_time=$1\&end_time=$2);
printf "%s\t%s\t%s\n" $1 $2 $count;
保存为queryDay.sh,脚本就是去调用接口获取单量,然后输出,单次调用效果如下:
$ bash queryDay.sh 2019-04-24T00:00:00 2019-04-25T00:00:00
2019-04-24T00:00:00 2019-04-25T00:00:00 12261
- 使用xargs并发3进程执行
$ ./genDay.sh |xargs -L1 -P3 ./queryDay.sh | pv -l -s 365 > data.txt
261 0:00:18 [14.6 /s] [================================================> ] 71% ETA 0:00:07
可以看到,由于使用了3个进程执行,处理速度明显变快了,之前处理到70%进度用了37s,这次只用了18s,之前处理qps是6.98/s,这次qps是14.6/s。
其中,xargs命令的作用是把输入流的数据,转化为后面脚本的参数,而-L1参数,表示每次读取1行数据,并将这一行数据按空白拆成参数传给queryDay.sh,-P3参数,表示使用3个进程来执行queryDay.sh。
总结
Shell脚本可以快速实现一些简单的小功能,简单学习了解一下,是非常值得的。
往期内容
提高工作效率,这些Linux命令可以帮到你(一)
这些实用的Linux技巧,看你知道几个!
长按关注【打码日记】