提交 7e42ea1a 编写于 作者: 爱吃苦瓜的猿's avatar 爱吃苦瓜的猿

add extract_part_lines

上级 22f59365
# linux
# linux脚本工具片
## [linux启动停止脚本实现](linux/start_stop_script/README.md)
## [shell实现文件分段提取](linux/extract_part_lines/README.md)
# 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
```
### 脚本示例与说明
[代码地址](https://gitcode.net/eaglejava2015/eagle-blog/-/blob/master/linux/extract_part_lines/extract_part_lines.sh)
我们一般情况都需要备份源文件,以防直接替换源文件出现问题有无法恢复源文件,所以今天我们给题主的需求中加一个备份(这一步属于建议)。
```shell
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`
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."
.
.
.
Package: linux-headers-abc
.
.
.
Build-Profiles: abc
Package: linux-image-*
.
.
.
Build-Profiles: abc
Package: linux-headers-def
.
.
.
Build-Profiles: def
.
.
.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册