如何基于CSV第5列值重排行?现有Shell方案需优化
问题:根据CSV第5列键值重排行数据,将目标行移至顶部
需求说明
- 处理CSV文件
test.csv,把第1列值等于第5列唯一键值的行移到所有数据行最前面 - 保留原表头,其余数据行维持原有顺序
测试文件内容 (test.csv)
Col A,Col B,Col C,Col D,Col E A,Data 1,Category 1,Name 1,C B,Data 2,Category 2,Name 2,C C,Data 3,Category 3,Name 3,C D,Data 4,Category 4,Name 4,C E,Data 5,Category 5,Name 5,C F,Data 6,Category 6,Name 6,C
期望输出
Col A,Col B,Col C,Col D,Col E C,Data 3,Category 3,Name 3,C A,Data 1,Category 1,Name 1,C B,Data 2,Category 2,Name 2,C D,Data 4,Category 4,Name 4,C E,Data 5,Category 5,Name 5,C F,Data 6,Category 6,Name 6,C
原实现代码(生成多个临时文件)
sed 1d test.csv > input.csv key=`awk -F"," -v 'OFS=,' '{ print $5}' input.csv | uniq` awk -F"," -v 'OFS=,' '{if($1 == "'$key'") print}' input.csv > temp.csv cat temp.csv input.csv > temp2.csv awk '!seen[$0]++' temp2.csv > output.csv sed -i '1iCol A,Col B,Col C,Col D,Col E' output.csv
优化方案(无临时文件,单Awk命令搞定)
用Awk一次性完成所有逻辑,不需要生成任何临时文件,代码如下:
awk -F',' -v OFS=',' ' NR == 1 { print; next } NR == 2 { key = $5 } $1 == key { target = $0; next } { lines[++n] = $0 } END { print target; for (i=1; i<=n; i++) print lines[i] } ' test.csv > output.csv
代码逻辑解释
- 第一行是表头,直接输出后跳过后续处理
- 从第二行(第一行数据)提取第5列的键值(默认所有数据行第5列值一致)
- 匹配到第1列等于键值的行,单独存到变量里,不加入普通行数组
- 所有非目标行存入数组,记录行数
- 最后先输出目标行,再依次输出数组里的其余行
如果需要直接覆盖原文件(注意提前备份),可以用sponge命令(需安装moreutils包):
awk -F',' -v OFS=',' ' NR == 1 { print; next } NR == 2 { key = $5 } $1 == key { target = $0; next } { lines[++n] = $0 } END { print target; for (i=1; i<=n; i++) print lines[i] } ' test.csv | sponge test.csv
内容的提问来源于stack exchange,提问作者zxcv
相关产品推荐
相关产品推荐

