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

如何在awk数组遍历中实现GNU sort -V风格的字母数字混合排序

awk实现chr序列ID自然排序方案(兼容GNU sort -V效果)

核心逻辑

自然排序和普通字符串/数值排序的核心差异在于对混合字母数字的字段拆分规则:将序列ID拆分为交替的纯字符串段、纯数字段,按位置逐个比较:

  • 字符串段按ASCII码字典序比较
  • 数字段按数值大小比较
  • 主chr编号比较完成后,_alt、_random等后缀按同样规则继续比较,最终排序结果和GNU sort -V完全一致

完整实现代码

#!/usr/bin/awk -f

# 自定义chr ID比较函数,返回值规则:a<b返回-1,a==b返回0,a>b返回1
function cmp_chr(a, b,    a_len, b_len, a_seg, b_seg, a_is_num, b_is_num, i) {
    # 拆分a为交替的数字/非数字段
    a_len = 0
    while (match(a, /([^0-9]+)|([0-9]+)/, seg)) {
        a_seg[++a_len] = seg[0]
        a = substr(a, RSTART + RLENGTH)
    }
    # 拆分b为交替的数字/非数字段
    b_len = 0
    while (match(b, /([^0-9]+)|([0-9]+)/, seg)) {
        b_seg[++b_len] = seg[0]
        b = substr(b, RSTART + RLENGTH)
    }
    # 逐个段比较
    for (i = 1; i <= a_len && i <= b_len; i++) {
        a_is_num = (a_seg[i] ~ /^[0-9]+$/)
        b_is_num = (b_seg[i] ~ /^[0-9]+$/)
        if (a_is_num && b_is_num) {
            # 都是数字按数值比较
            if (a_seg[i] + 0 < b_seg[i] + 0) return -1
            if (a_seg[i] + 0 > b_seg[i] + 0) return 1
        } else {
            # 有一个不是数字按字符串比较
            if (a_seg[i] < b_seg[i]) return -1
            if (a_seg[i] > b_seg[i]) return 1
        }
    }
    # 前面段都相同,短的排前面
    if (a_len < b_len) return -1
    if (a_len > b_len) return 1
    return 0
}

# 读入所有行,第一列是序列ID,存入数组
{
    lines[NR] = $0
    ids[NR] = $1
}

# 所有行读入完成后,自定义排序输出
END {
    # 冒泡排序实现(数据量小的场景可用,数据量大可替换为快速排序实现)
    for (i = 1; i < NR; i++) {
        for (j = i + 1; j <= NR; j++) {
            if (cmp_chr(ids[i], ids[j]) > 0) {
                # 交换ID和对应行
                tmp_id = ids[i]
                ids[i] = ids[j]
                ids[j] = tmp_id
                tmp_line = lines[i]
                lines[i] = lines[j]
                lines[j] = tmp_line
            }
        }
    }
    # 输出排序后结果
    for (i = 1; i <= NR; i++) {
        print lines[i]
    }
}

效果验证

输入样例

chr10_random
chr1
chr2
chrM
chr10
chr22
chr1_alt
chrUn_gl000220
chr1_random

输出结果(和sort -V结果完全一致)

chr1
chr1_alt
chr1_random
chr2
chr10
chr10_random
chr22
chrM
chrUn_gl000220

注意事项

  • 代码使用了GNU awk的match()捕获组特性,需使用gawk运行
  • 示例中用冒泡排序仅为简化实现,处理万级以上行时建议替换为更高效的快速排序实现
  • 若序列ID不在第一列,只需修改ids[NR] = $1为对应的列号即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 07:54:05