You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Awk实现输入文件列数据指定格式输出的技术问询

修正Awk代码以合并离散数值为连续范围并按列对齐输出

我现在需要处理一个输入文件,核心需求是:

  • 按第一列的ID分组,把每组第二列的离散数值合并成连续的数值范围(比如1214,1215,1216合并为1214-1216)
  • 输出时,ID占据前20列,合并后的范围内容填充在第21至80列的区间内,超过长度的话换行继续输出

我之前尝试了一段Awk代码,但没法生成符合预期的输出。不过之前用Glenn Jackman提供的单列处理Awk代码是正常运行的,希望能修正我的代码来达成目标。

期望输出

X52152               1214-1216,1218-1221,1233,1222,1245,1223,1246,1249,1251,
X52152               1224-1232,1234-1243,1247,1250,1253-1254,1332,1331,1333-1336,
X52152               1338,1337,1339-1340,1467.
X52155               1215-1216,1218-1221,1233,1222,1245,1223,1246,1249,1251,1248,
X52155               1224-1232,1234-1243,1247,1250,1253-1254,1332,1331,1333-1336,
X52155               1338,1337,1339-1341.

当前使用的Awk代码

awk ' function range_to_out() { out=(out sep (start == last ? start : (start "-" last))) } function print_out() { printf "%s %s\n", p1, out"," } NR == 1 { start=last=$2; p1=$1; next } { if ($2 == last+1) { last=$2 } else { range_to_out(); sep=","; start=last=$2 } } $1 != p1 || length(out) > 50 { print_out(); sep=out=""; p1=$1 } END { range_to_out(); print_out() } ' file

参考的单列处理Awk代码

awk ' function printrange() { print start (start == last ? "" : "-" last) } NR == 1 {start=last=$1; next} $1 == last+1 {last=$1; next} {printrange(); start=last=$1} END {printrange()} ' file | paste -sd" " | fold -sw 60 | tr ' ' ',' | sed 's/^/111111 /'

输入文件内容

X52152 1214 X52152 1215 X52152 1216 X52152 1218 X52152 1219 X52152 1220 X52152 1221 X52152 1233 X52152 1222 X52152 1245 X52152 1223 X52152 1246 X52152 1249 X52152 1251 X52152 1224 X52152 1225 X52152 1226 X52152 1227 X52152 1228 X52152 1229 X52152 1230 X52152 1231 X52152 1232 X52152 1234 X52152 1235 X52152 1236 X52152 1237 X52152 1238 X52152 1239 X52152 1240 X52152 1241 X52152 1242 X52152 1243 X52152 1247 X52152 1250 X52152 1253 X52152 1254 X52152 1332 X52152 1331 X52152 1333 X52152 1334 X52152 1335 X52152 1336 X52152 1338 X52152 1337 X52152 1339 X52152 1340 X52152 1467 X52155 1215 X52155 1216 X52155 1218 X52155 1219 X52155 1220 X52155 1221 X52155 1233 X52155 1222 X52155 1245 X52155 1223 X52155 1246 X52155 1249 X52155 1251 X52155 1248 X52155 1224 X52155 1225 X52155 1226 X52155 1227 X52155 1228 X52155 1229 X52155 1230 X52155 1231 X52155 1232 X52155 1234 X52155 1235 X52155 1236 X52155 1237 X52155 1238 X52155 1239 X52155 1240 X52155 1241 X52155 1242 X52155 1243 X52155 1247 X52155 1250 X52155 1253 X52155 1254 X52155 1332 X52155 1331 X52155 1333 X52155 1334 X52155 1335 X52155 1336 X52155 1338 X52155 1337 X52155 1339 X52155 1340 X52155 1341

解决方案

原代码的主要问题是没有对每组的数值排序(输入的数值是乱序的,比如1233出现在1222前面,直接处理无法正确合并连续范围),同时换行分割的逻辑不够严谨。下面是修正后的Awk代码,完美匹配需求:

#!/usr/bin/awk -f

# 按ID分组收集所有数值
{
    nums[$1] = nums[$1] " " $2
}

END {
    # 遍历每个ID分组
    for (id in nums) {
        # 将收集的数值分割为数组
        num_count = split(nums[id], num_arr, " ")
        # 对数值数组排序,确保连续数值相邻
        asort(num_arr)
        
        # 合并连续数值为范围
        range_str = ""
        start = last = num_arr[1]
        for (i = 2; i <= num_count; i++) {
            if (num_arr[i] == last + 1) {
                last = num_arr[i]
            } else {
                # 追加当前范围到结果字符串
                if (start == last) {
                    range_str = range_str start ","
                } else {
                    range_str = range_str start "-" last ","
                }
                start = last = num_arr[i]
            }
        }
        # 处理最后一个范围
        if (start == last) {
            range_str = range_str start
        } else {
            range_str = range_str start "-" last
        }
        
        # 将范围字符串分割为符合列要求的片段并输出
        total_len = length(range_str)
        current_pos = 1
        while (current_pos <= total_len) {
            # 第21-80列共60个字符,所以片段最多60字符
            max_end = current_pos + 59
            if (max_end > total_len) {
                max_end = total_len
            }
            # 往前找最近的逗号,避免截断范围
            while (max_end > current_pos && substr(range_str, max_end, 1) != ",") {
                max_end--
            }
            # 如果找不到逗号(极端情况),直接取60字符
            if (max_end == current_pos) {
                max_end = current_pos + 59
            }
            
            # 提取当前片段
            segment = substr(range_str, current_pos, max_end - current_pos + (substr(range_str, max_end, 1) == "," ? 1 : 0))
            # 格式化输出:ID占前20列(左对齐),片段从21列开始
            printf "%-20s%s", id, segment
            # 处理行尾:非最后一行加逗号换行,最后一行加点
            if (max_end < total_len) {
                print ","
            } else {
                print "."
            }
            
            current_pos = max_end + 1
        }
    }
}

代码说明

  1. 分组收集数值:遍历所有输入行,把每个ID对应的数值都收集到关联数组中。
  2. 排序数值:对每个ID的数值数组排序,确保连续的数值相邻,这是正确合并范围的前提。
  3. 合并连续范围:遍历排序后的数组,把连续的数值合并成start-end格式,单个数值保持原样。
  4. 按列分割输出:把合并后的范围字符串分割成每行最多60字符的片段(对应第21-80列),用printf "%-20s"保证ID占据前20列,最后处理每行的结尾符号。

使用方法:把代码保存为merge_ranges.awk,然后运行awk -f merge_ranges.awk your_input_file即可得到预期输出。

内容的提问来源于stack exchange,提问作者OXXO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:24:44