如何用AWK或Sed将非结构化数据转换为表格格式?
动态转换键值对数据为表格格式的Shell脚本实现
需求描述
输入数据为按Row分组的键值对格式,每个字段单独一行:
Row: 1 Name : Ajay Age : 45 Address : zyx Row: 2 Name : Ajay Age : 45 Address : zyx Row: 3 Name : Ajay Age : 45 Address : zyx Row: 4 Name : Ajay Age : 45 Address : zyx
需要将其转换为带表头、分隔线的表格格式,且支持任意列名和列数:
Name Age Address ----- ---- ------- Ajay 45 zyx Ajay 45 zyx Ajay 45 zyx Ajay 45 zyx
原脚本存在的问题
你提供的脚本有以下核心问题:
- 错误假设第一个
Row下的所有字段在同一行,实际每个字段单独一行,导致只能提取到第一个列名 - 缺少表头分隔线的生成逻辑
- 循环中多次调用
sed和awk,处理大文件时效率低下
改进后的脚本
#!/bin/bash data_file="ab.txt" # 提取第一个Row块中的所有列名 column_names=() while IFS= read -r line; do if [[ "$line" =~ ^Row: ]]; then # 读取当前Row块内的所有字段行,直到空行 while IFS= read -r field_line && [[ -n "$field_line" ]]; do # 提取字段名(去除前后空格) col_name=$(awk -F ':' '{gsub(/^[ \t]+|[ \t]+$/, "", $1); print $1}' <<< "$field_line") column_names+=("$col_name") done break # 仅提取第一个Row的列名作为表头 fi done < "$data_file" # 打印对齐的表头 printf "%-12s" "${column_names[@]}" printf "\n" # 打印表头分隔线(与列名长度匹配) for name in "${column_names[@]}"; do printf "%-12s" "$(printf "%0.s-" $(seq 1 ${#name}))" done printf "\n" # 一次性解析所有数据并打印表格行 awk -v col_count="${#column_names[@]}" ' BEGIN { current_row = 0; field_pos = 0 } /^Row:/ { current_row++; field_pos = 0; next } /^[[:space:]]*$/ { next } # 跳过空行 { # 分割键值对,去除值的前后空格 split($0, kv, /:[[:space:]]+/) gsub(/^[[:space:]]+|[[:space:]]+$/, "", kv[2]) row_data[current_row][++field_pos] = kv[2] # 当收集完当前行所有字段后,打印整行 if (field_pos == col_count) { for (i=1; i<=col_count; i++) { printf "%-12s", row_data[current_row][i] } printf "\n" } } ' "$data_file"
脚本说明
- 列名提取:通过循环读取文件,定位第一个
Row块,逐行解析每个字段的名称并存储到数组,确保支持任意列数。 - 表头与分隔线:用
printf的格式化输出实现列对齐,分隔线长度与对应列名长度一致。 - 数据解析:使用
awk一次性处理整个文件,按Row分组收集数据,当某一行的所有字段收集完成后统一打印,提升处理效率。
内容的提问来源于stack exchange,提问作者Priyanka
相关产品推荐
相关产品推荐

