文件比对shell脚本实战(多线程并发shell)

简介: 文件比对shell脚本实战(多线程并发shell)

需求

1、在A、B两个AP的log中筛出某个关键字,将比较结果输出

输入:

A log

2023-02-01 17:13:51.988  INFO 48500 --- [pool-1-thread-1707] c.n.fileloader.service.RabbitMQService   : [PARAM-PRINT] 文件名:A1450AOIH05.TXT 开始行:1007  剩余解析数量:0
2023-02-01 17:13:51.997  INFO 48500 --- [pool-1-thread-2942] c.n.fileloader.service.RabbitMQService   : [PARAM-PRINT] 文件名:L2111SEAI01.TXT 开始行:7267  剩余解析数量:1

B log

2023-02-01 17:13:51.988  INFO 48500 --- [pool-1-thread-1707] c.n.fileloader.service.RabbitMQService   : [PARAM-PRINT] 文件名:A1450AOIH05.TXT 开始行:1008  剩余解析数量:0
2023-02-01 17:13:51.997  INFO 48500 --- [pool-1-thread-2942] c.n.fileloader.service.RabbitMQService   : [PARAM-PRINT] 文件名:L2111SEAI01.TXT 开始行:7267  剩余解析数量:1

输出

如果两个日志文件中的指定key的value相等则为true,否则为false

A1450AOIH05 1007 1008 false

2、A和B 中是否有关键字 “开始解析”

            A B 
C1310MACR02 0 0
C1310MACR02 0 1
C1310MACR02 1 1

3、B 中是否有关键字 “本次解析结束”

          B 
C1310MACR02 0
indexname 1

生成index脚本

#!/bin/bash
# author: ninesun
# date: 2023年2月8日08:55:21
# desc: generate fl redis key
indexprefix=${1-'/202302/08'}
echo '' > /tmp/indexfile
echo '' > /tmp/startline.txt
echo '' > /tmp/lineurl.txt
echo "start generate ..."
# acf
cd /dfs/acf/INDEX/${indexprefix}
for i in `ls | grep -E "^A|^C"`;do echo START_LINE:/INDEX${indexprefix}/$i >> /tmp/startline.txt ;done
for i in `ls | grep -E "^A|^C"`;do echo LINE_URL:/INDEX/202302/02/$i >> /tmp/lineurl.txt ;done
# oc
cd /dfs/oc/INDEX/${indexprefix}
for i in `ls | grep -E "^L"`;do echo LINE_URL:/INDEX${indexprefix}/$i >> /tmp/lineurl.txt ;done
for i in `ls | grep -E "^L"`;do echo START_LINE:/INDEX${indexprefix}/$i >> /tmp/startline.txt ;done
cat /tmp/lineurl.txt | cut -d / -f5| grep -Ev '^$' >> /tmp/indexfile
qty=$(wc -l /tmp/indexfile)
echo "end generate ...,total index: ${qty}"

生成的记过类似于这个. 下面的

shell脚本实现

根据index比较新旧两个log的差异

#!/bin/bash
# date: 2023年2月1日17:48:37
# author: ninesun
# para: 1.old-fl.log 2.new-fl.log 3. indexFile
set -e
pushd `dirname $0` > /dev/null
SCRIPT_PATH=`pwd -P`
popd > /dev/null
SCRIPT_FILE=`basename $0`
if [ $# -eq 3 ];then 
    oldLog=${1-'/dev/null'}
    newLog=$2
    indexFile=$3
else 
    echo "参数有误"
    exit 8
fi
oldtar=/tmp/old-filter
newtar=/tmp/nwe-filter
startparsekey="开始解析:"
parseendkey="本次解析结束"
part1key="文件名:"
part1key2=" 开始行"
strparseoldtar=/tmp/old-strparse
strparsenewtar=/tmp/nwe-strparse
parseendtar=/tmp/parseend-tar
parseendresult=/tmp/parseend-result
echo '' >${oldtar}
echo '' >${newtar}
echo '' >${strparsenewtar}
echo '' >${strparseoldtar}
echo '' >${parseendtar}
echo '' > /tmp/fl-com-result
echo '' > /tmp/fl-com-result-parse
echo '' > ${parseendresult}
echo "begin filter" 
date +"%F %T"
while read line;do
    # 遍历A old log
    oldv=$(grep -E ${part1key}${line} ${oldLog}  | tail -n1 | awk '{print $(NF-2),$(NF-1)}' | tr -dc '0-9.a-zA-Z' | sed 's/txt/txt,/gi')
    # 判空
    [[ -n ${oldv} ]] && echo ${oldv} >> ${oldtar} || echo "${line},NA" >>${oldtar}  
    # 遍历B new log
    newv=$(grep -E ${part1key}${line}  ${newLog}  | tail -n1  |awk '{print $(NF-2),$(NF-1)}' | tr -dc '0-9.a-zA-Z' | sed 's/txt/txt,/gi')
    # 判空
    [[ -n ${newLog} ]] && echo ${newv} >> ${newtar} ||  echo "${line},NA" >>${newtar} 
    start_parse_line=$(echo ${line} | cut -d . -f1)
done < ${indexFile}
date +"%F %T"
echo "END filter"
function merge () {
    for old in `cat ${oldtar}`;do
        for new in `cat ${newtar}`;do
            #echo "old:$old new:$new"
            indexOld=`echo ${old} | cut -d , -f1`
            indexOldVal=`echo ${old} | cut -d , -f2`
            indexNew=`echo ${new} | cut -d , -f1`
            indexNewVal=`echo ${new} | cut -d , -f2`
            #echo "${indexOld} ${indexOldVal} ${indexNew} ${indexNewVal}"
             if [[ ${indexOld} == ${indexNew} && ${indexOldVal} == ${indexNewVal} ]];then
                echo "${indexOld},${indexOldVal},${indexNewVal},true"  >>/tmp/fl-com-result
                break;
             fi                
             if [[ ${indexOld} == ${indexNew} && ${indexOldVal} != ${indexNewVal} ]];then
                 echo "${indexOld},${indexOldVal},${indexNewVal},false"  >>/tmp/fl-com-result
                 break;
             fi
        done
    done
}
function begin-parse () {
    grep -E ${startparsekey} ${oldLog}  | awk '{print $NF}'  | awk -F : '{print $NF}' >> ${strparseoldtar}
    grep -E ${startparsekey} ${newLog}  | awk '{print $NF}'  | awk -F : '{print $NF}' >> ${strparsenewtar} 
    for old in `cat ${strparseoldtar}`;do
         #echo "old:$old"
         # grep ${old} ${strparsenewtar} |wc -l
         if [[ `grep ${old} ${strparsenewtar} |wc -l` -eq 1 ]];then
            echo "${old},1,1"  >>/tmp/fl-com-result-parse
         else
             echo "${old},1,0"  >>/tmp/fl-com-result-parse
         fi
    done
}
function parseend(){
    grep -E ${parseendkey} ${newLog}  | awk '{print $NF}'  | awk -F / '{print $NF}' >> ${parseendtar}   
    for line in `cat ${parseendtar}`;do 
       #echo $line
      if [[ `grep ${line} ${indexFile} |wc -l` -eq 1 ]];then
        echo "${line},1" >> ${parseendresult}
      else
        echo "${line},0" >> ${parseendresult}
      fi   
    done  
}
echo "----------------------------------------------"
echo "part1 begin"
date +"%F %T"
merge
date +"%F %T"
echo "part1 end,please check,path is : /tmp/fl-com-result"
echo "----------------------------------------------"
echo "part2 begin"
date +"%F %T"
begin-parse
date +"%F %T"
echo "part2 end,please check,path is : /tmp/fl-com-result-parse"
echo "----------------------------------------------"
echo "part3 begin"
date +"%F %T"
parseend
date +"%F %T"
echo "part3 end,please check,path is : /tmp/parseend-result"

输出字段解释

index,Alog 行号,Blog行号,是否一致

运行脚本:

第1、2个参数是输入的AP日志

第3个参数是变量的indexList(你理解为一个关键字就可以)

bash fl-compare.sh info.log.2023-02-02.12.log info.log.2023-02-02.0.log indexfile
begin filter
2023-02-06 15:07:13
2023-02-06 15:11:31
END filter
----------------------------------------------
part1 begin
2023-02-06 15:11:31
2023-02-06 16:21:23
part1 end,please check,path is : /tmp/fl-com-result
----------------------------------------------
part2 begin
2023-02-06 16:21:23
2023-02-06 16:22:26
part2 end,please check,path is : /tmp/fl-com-result-parse
----------------------------------------------
part3 begin
2023-02-06 16:22:26
2023-02-06 16:22:37
part3 end,please check,path is : /tmp/parseend-result

ae5d9dcccc5040a7b4dfd3c45291361e.png

多线程并发shell实现

测试的log大概100行,而正式区的log大约在100W行左右,甚至更多。

当在正式区单个日志大概158W, 也就是300 W 的级别文本比较。


这时候如果你的机器是多个核心,就可以充分利用并发处理来加快速度。

以下代码为优化后的多线程并发脚本

#!/bin/bash
# date: 2023年2月1日17:48:37
# author: ninesun
# para: 1.old-fl.log 2.new-fl.log 3. indexFile
set -e
pushd `dirname $0` > /dev/null
SCRIPT_PATH=`pwd -P`
popd > /dev/null
SCRIPT_FILE=`basename $0`
if [ $# -eq 4 ];then 
    oldLog=${1-'/dev/null'}
    newLog=$2
    indexFile=$3
    threadCount=${4-'5'} # 默认五个线程
else 
    echo "参数有误"
    exit 8
fi
rm -rf /tmp/fl.fifo
fifoname=/tmp/fl.fifo
mkfifo ${fifoname}
exec 8<> ${fifoname}
echo "thread count is total ${threadCount}"
for line in `seq ${threadCount}`;do
        echo >&8
done
oldtar=/tmp/old-filter
newtar=/tmp/nwe-filter
startparsekey="开始解析:"
parseendkey="本次解析结束"
part1key="文件名:"
part1key2=" 开始行"
strparseoldtar=/tmp/old-strparse
strparsenewtar=/tmp/nwe-strparse
parseendtar=/tmp/parseend-tar
parseendresult=/tmp/parseend-result
echo '' >${oldtar}
echo '' >${newtar}
echo '' >${strparsenewtar}
echo '' >${strparseoldtar}
echo '' >${parseendtar}
echo '' > /tmp/fl-com-result
echo '' > /tmp/fl-com-result-parse
echo '' > ${parseendresult}
echo "begin filter" 
date +"%F %T"
while read line;do
     read -u 8 # 从文件描述符8中读取一行
     {
            # 遍历A old log
        oldv=$(grep -E ${part1key}${line} ${oldLog}  | tail -n1 | awk '{print $(NF-2),$(NF-1)}' | tr -dc '0-9.a-zA-Z' | sed 's/txt/txt,/gi')
        # 判空
        [[ -n ${oldv} ]] && echo ${oldv} >> ${oldtar} || echo "${line},NA" >>${oldtar}  
        # 遍历B new log
        newv=$(grep -E ${part1key}${line}  ${newLog}  | tail -n1  |awk '{print $(NF-2),$(NF-1)}' | tr -dc '0-9.a-zA-Z' | sed 's/txt/txt,/gi')
        # 判空
        [[ -n ${newLog} ]] && echo ${newv} >> ${newtar} ||  echo "${line},NA" >>${newtar} 
        start_parse_line=$(echo ${line} | cut -d . -f1)
        echo >&8 
     } &
done < ${indexFile}
# exec 8>&-
wait
date +"%F %T"
echo "END filter"
function merge () {
    for old in `cat ${oldtar}`;do
        read -u 8
        {
        for new in `cat ${newtar}`;do
                    #echo "old:$old new:$new"
                    indexOld=`echo ${old} | cut -d , -f1`
                    indexOldVal=`echo ${old} | cut -d , -f2`
                    indexNew=`echo ${new} | cut -d , -f1`
                    indexNewVal=`echo ${new} | cut -d , -f2`
                    #echo "${indexOld} ${indexOldVal} ${indexNew} ${indexNewVal}"
                    if [[ ${indexOld} == ${indexNew} && ${indexOldVal} == ${indexNewVal} ]];then
                        echo "${indexOld},${indexOldVal},${indexNewVal},true"  >>/tmp/fl-com-result
                        break;
                    fi                
                    if [[ ${indexOld} == ${indexNew} && ${indexOldVal} != ${indexNewVal} ]];then
                        echo "${indexOld},${indexOldVal},${indexNewVal},false"  >>/tmp/fl-com-result
                        break;
                    fi
                done
            echo >&8                
        }&        
    done
    exec 8>&-
    wait
}
function begin-parse () {
    grep -E ${startparsekey} ${oldLog}  | awk '{print $NF}'  | awk -F : '{print $NF}' >> ${strparseoldtar}
    grep -E ${startparsekey} ${newLog}  | awk '{print $NF}'  | awk -F : '{print $NF}' >> ${strparsenewtar} 
    for old in `cat ${strparseoldtar}`;do
         #echo "old:$old"
         # grep ${old} ${strparsenewtar} |wc -l
         if [[ `grep ${old} ${strparsenewtar} |wc -l` -eq 1 ]];then
            echo "${old},1,1"  >>/tmp/fl-com-result-parse
         else
             echo "${old},1,0"  >>/tmp/fl-com-result-parse
         fi
    done
}
function parseend(){
    grep -E ${parseendkey} ${newLog}  | awk '{print $NF}'  | awk -F / '{print $NF}' >> ${parseendtar}   
    for line in `cat ${parseendtar}`;do 
       #echo $line
      if [[ `grep ${line} ${indexFile} |wc -l` -eq 1 ]];then
        echo "${line},1" >> ${parseendresult}
      else
        echo "${line},0" >> ${parseendresult}
      fi   
    done  
}
echo "----------------------------------------------"
echo "part1 begin"
date +"%F %T"
merge
date +"%F %T"
echo "part1 end,please check,path is : /tmp/fl-com-result"
echo "----------------------------------------------"
echo "part2 begin"
date +"%F %T"
begin-parse
date +"%F %T"
echo "part2 end,please check,path is : /tmp/fl-com-result-parse"
echo "----------------------------------------------"
echo "part3 begin"
date +"%F %T"
parseend
date +"%F %T"
echo "part3 end,please check,path is : /tmp/parseend-result"

性能比较

测试机器配置:

48c256g

以下测试是 并发20的case

20代表并发数

bash fl-compare-multiprocess.sh info.log.2023-02-02.0.log info.log.2023-02-02.12.log indexfile 20

cpu大致能打到50%.

运行输出

thread count is total 20
begin filter
2023-02-06 15:35:07
2023-02-06 15:35:23
END filter
----------------------------------------------
part1 begin
2023-02-06 15:35:23
2023-02-06 15:41:07
part1 end,please check,path is : /tmp/fl-com-result
----------------------------------------------
part2 begin
2023-02-06 15:41:07
2023-02-06 15:42:25
part2 end,please check,path is : /tmp/fl-com-result-parse
----------------------------------------------
part3 begin
2023-02-06 15:42:25
2023-02-06 15:42:25
part3 end,please check,path is : /tmp/parseend-result

40代表并发数测试

bash fl-compare-multiprocess.sh info.log.2023-02-02.0.log info.log.2023-02-02.12.log indexfile 40

cpu大致能打到90%往上

prometheus监控看到的结果

97bd9f8b0d364091b53f9e30163ab6af.png

51e9ca93c33748d99677e16c74ac5929.png

重新开启一个窗口看进程数变化

]#while true;do ps -ef | grep "fl-compare-multiprocess.sh info"  | grep -v grep |wc -l; sleep 1s;done
21
20
21
21
21
21
21
21
21
21
21
21
21
21
21
21
21
21
21
21
21
....

优化前后性能比对

20个thread的情况下大致是10倍的速度提升.

参考

parallel工具

目录
相关文章
|
2月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
178 2
|
3月前
|
存储 安全 Unix
七、Linux Shell 与脚本基础
别再一遍遍地敲重复的命令了,把它们写进Shell脚本,就能一键搞定。脚本本质上就是个存着一堆命令的文本文件,但要让它“活”起来,有几个关键点:文件开头最好用#!/usr/bin/env bash来指定解释器,并用chmod +x给它执行权限。执行时也有讲究:./script.sh是在一个新“房间”(子Shell)里跑,不影响你;而source script.sh是在当前“房间”里跑,适合用来加载环境变量和配置文件。
|
5月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
369 83
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
290 0
|
3月前
|
存储 Shell Linux
八、Linux Shell 脚本:变量与字符串
Shell脚本里的变量就像一个个贴着标签的“箱子”。装东西(赋值)时,=两边千万不能有空格。用单引号''装进去的东西会原封不动,用双引号""则会让里面的$变量先“变身”再装箱。默认箱子只能在当前“房间”(Shell进程)用,想让隔壁房间(子进程)也能看到,就得给箱子盖个export的“出口”戳。此外,Shell还自带了$?(上条命令的成绩单)和$1(别人递进来的第一个包裹)等许多特殊箱子,非常有用。
|
5月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
345 83
|
4月前
|
数据采集 消息中间件 并行计算
Python多线程与多进程性能对比:从原理到实战的深度解析
在Python编程中,多线程与多进程是提升并发性能的关键手段。本文通过实验数据、代码示例和通俗比喻,深入解析两者在不同任务类型下的性能表现,帮助开发者科学选择并发策略,优化程序效率。
304 1
|
3月前
|
数据采集 监控 Shell
无需Python:Shell脚本如何成为你的自动化爬虫引擎?
Shell脚本利用curl/wget发起请求,结合文本处理工具构建轻量级爬虫,支持并行加速、定时任务、增量抓取及分布式部署。通过随机UA、异常重试等优化提升稳定性,适用于日志监控、价格追踪等场景。相比Python,具备启动快、资源占用低的优势,适合嵌入式或老旧服务器环境,复杂任务可结合Python实现混合编程。
|
5月前
|
Web App开发 缓存 安全
Linux一键清理系统垃圾:释放30GB空间的Shell脚本实战​
这篇博客介绍了一个实用的Linux系统盘清理脚本,主要功能包括: 安全权限检查和旧内核清理,保留当前使用内核 7天以上日志文件清理和系统日志压缩 浏览器缓存(Chrome/Firefox)、APT缓存、临时文件清理 智能清理Snap旧版本和Docker无用数据 提供磁盘空间使用前后对比和大文件查找功能 脚本采用交互式设计确保安全性,适合定期维护开发环境、服务器和个人电脑。文章详细解析了脚本的关键功能代码,并给出了使用建议。完整脚本已开源,用户可根据需求自定义调整清理策略。
629 1