shell实现文件分段提取

近期在问答模块看到一个小伙伴在问,如何从一个配置文件中提取出指定的几个片段。每个片段的起止行都有可识别的标识。

回到完问题后,决定把这个答题过程写得更清楚一点,以供后期查看和大家参考。

使用场景说明

平时工作中可能会遇到一些超大的配置文件。
很多时候我们只关注其中某些特别的部分,希望从中抽取出来单独分析。

案例解析

问题描述:

题主想从下面一个配置文件中提取每个Package: linux-headers-的配置内容(每段配置的起始行为:Package: linux-headers-,结束行为Build-Profiles: *)
并且把非目标行删除。

原始文件样式如下:

.
.
.
Package: linux-headers-abc
.
.
.
Build-Profiles: abc

Package: linux-image-*
.
.
.
Build-Profiles: abc

Package: linux-headers-def
.
.
.
Build-Profiles: def

.
.
.

目标结果如下:

Package: linux-headers-abc
.
.
.
Build-Profiles: abc
Package: linux-headers-def
.
.
.
Build-Profiles: def

解题思路

1-获取起始行:Package: linux-headers-的行号列表
2-获取结尾行:Build-Profiles的行号列表
3-用2层循环找1和2组成的起始行号对(比如起始行号为10,那就要找到第一个大于10的\n行号,为其对应的截止行号)。
4-遍历上面的起始行号对,倒序删除。加入组成的对为5,10,20,25.
那么先删除25-最后
在删除10-20
再删除首行-5
这样剩下的就是要保留的

注意事项

1.删除时记得一定要倒序删,正序前面的行号删掉后,后面的行号就变了,倒序删就没有这个问题
2.首行前面是否需要删除需要判断是否满足 第一个行号 > 1

脚本示例与说明

代码地址

我们一般情况都需要备份源文件,以防直接替换源文件出现问题有无法恢复源文件,所以今天我们给题主的需求中加一个备份(这一步属于建议)。

filename=$1

# 操作前备份一下源文件
bak_filename=${filename}.bak
cp ${filename} ${bak_filename}

# 获取起始行集合
beginlines=$(grep -n 'Package: linux-headers-*' ${filename} | cut -d : -f 1)
endlines=$(grep -n 'Build-Profiles:' ${filename} | cut -d : -f 1)


# 获取起始行对
begin_end_arr=()
for i in ${beginlines[*]}; do
    for j in ${endlines[*]}; do
        if [ $j -gt $i ]; then
            # 在结尾行集合中找到首个大于起始行号的行,并认定为对应结尾行号,一起加入到结果对中
            begin_end_arr[${#begin_end_arr[*]}]=$i
            begin_end_arr[${#begin_end_arr[*]}]=$j
            echo begin_end_arr
            break;
        fi
    done
done

# 倒序遍历删除不匹配的行,一次遍历一对
#${#array[@]}获取数组长度用于循环
arr_length=${#begin_end_arr[@]}
# 删除最后一行的下一行到文件文件尾部
lastline_num=${begin_end_arr[$((${arr_length}-1))]}
sed -i $((${lastline_num}+1))',$d' ${filename}

# 剔除出首尾行,中间按对删除
for ((i=$((${arr_length}-2));i>=1;i=i-2))
do
    begin=$((${begin_end_arr[i-1]}+1))
    end=$((${begin_end_arr[i]}-1))
    sed -i ${begin}','${end}'d' ${filename}
done


# 删除首行之前的全部行
firstline_num=${begin_end_arr[0]} 
[[ ${firstline_num} -gt 1 ]] && sed -i '1,'$((${firstline_num}-1))'d' ${filename}

echo "done."

使用示例sh extract_part_lines.sh test.conf

------ 2023年10月10日补充更新 ------
推荐更优的写法:参考评论区@大师兄打妖怪的评论,简单高效,优秀代码的典范,值得学习。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值