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

使用AWK按列名从空格分隔文件筛选列的问题

空格分隔压缩文件的列筛选脚本适配方案

问题场景

有一个空格分隔的压缩文件majorfile.gz,部分内容如下:

zcat majorfile.gz | head -n 3 | cut -d ' ' -f1-10

输出:

marker alleleA alleleB FINCH_WB_633_splitMerged FINCH_WB_633_splitMerged FINCH_WB_633_splitMerged FINCH_WB_C049985_splitMerged FINCH_WB_C049985_splitMerged FINCH_WB_C049985_splitMerged FINCH_WB_C071898_splitMerged
LR761571.1_34273 G C 0.9955 0.0045 0 0.9996 0.0004 0 1
LR761571.1_34285 G A 0.9934 0.0066 0 0.9999 0.0001 0 0.9435

需要根据header.subset.txt中的列名筛选列,该文件部分内容:

cat header.subset.txt | head

输出:

marker
alleleA
alleleB
FINCH_WB_633_splitMerged
FINCH_WB_ES1B002_splitMerged
FINCH_WB_JH1417_splitMerged
FINCH_WB_JH1452_splitMerged
FINCH_WB_JH1495_splitMerged
FINCH_WB_JP000_splitMerged
FINCH_WB_JP004_splitMerged

原循环脚本处理制表符分隔文件正常,但处理空格分隔文件时,会保留表头中所有同名的目标列(比如FINCH_WB_633_splitMerged出现三次就全部保留),且输出行末尾存在多余空格,结果不符合预期:

marker alleleA alleleB FINCH_WB_633_splitMerged FINCH_WB_633_splitMerged FINCH_WB_633_splitMerged
LR761571.1_34273 G C 0.9955 0.0045 0
LR761571.1_34285 G A 0.9934 0.0066 0

原AWK脚本(格式化后):

NR == FNR {
        a[$1]++
        next
}

{
        if (FNR == 1) {
                for (i = 1; i <= NF; i++) {
                        if (a[$i]) {
                                printf $i " "
                                b[i] = $i
                        }
                }
        } else {
                printf "\n"
                for (j = 1; j <= NF; j++) {
                        if (b[j]) {
                                printf $j " "
                        }
                }
        }
}

END {
        printf "\n"
}

问题原因

  1. 原脚本用a[$1]++统计列名出现次数,表头中重复列名会被多次匹配,导致所有同名列都被保留
  2. 输出逻辑直接追加空格,导致每行末尾存在多余空格
  3. 部分AWK版本不支持直接读取.gz压缩文件,存在兼容性隐患

解决方案

根据需求提供两种适配方案:

方案1:保留每个目标列名的第一次出现

如果需要对重复列名只保留表头中第一次出现的列,修改后的AWK脚本如下:

NR == FNR {
        target_cols[$1] = 1
        next
}

FNR == 1 {
        # 记录需要保留的列索引,匹配后清除标记避免重复选中
        for (i = 1; i <= NF; i++) {
                if (target_cols[$i]) {
                        keep_cols[i] = 1
                        delete target_cols[$i]
                }
        }
        # 输出表头,避免末尾空格
        first = 1
        for (i = 1; i <= NF; i++) {
                if (keep_cols[i]) {
                        printf "%s%s", first ? "" : " ", $i
                        first = 0
                }
        }
        printf "\n"
        next
}

# 处理数据行
{
        first = 1
        for (i = 1; i <= NF; i++) {
                if (keep_cols[i]) {
                        printf "%s%s", first ? "" : " ", $i
                        first = 0
                }
        }
        printf "\n"
}

方案2:保留所有匹配的列(包括同名列)

如果需要保留表头中所有同名的目标列,只需移除方案1中的delete target_cols[$i]即可:

NR == FNR {
        target_cols[$1] = 1
        next
}

FNR == 1 {
        # 记录所有需要保留的列索引
        for (i = 1; i <= NF; i++) {
                if (target_cols[$i]) {
                        keep_cols[i] = 1
                }
        }
        # 输出表头,避免末尾空格
        first = 1
        for (i = 1; i <= NF; i++) {
                if (keep_cols[i]) {
                        printf "%s%s", first ? "" : " ", $i
                        first = 0
                }
        }
        printf "\n"
        next
}

# 处理数据行
{
        first = 1
        for (i = 1; i <= NF; i++) {
                if (keep_cols[i]) {
                        printf "%s%s", first ? "" : " ", $i
                        first = 0
                }
        }
        printf "\n"
}

优化后的循环脚本

结合压缩文件读取兼容性和批量处理需求,修改循环脚本:

for subset_file in header.subset.txt; do
  # 生成带后缀的输出文件名
  output_file="majorfile_$(basename "$subset_file" .txt).gz"
  # 执行筛选并压缩输出
  awk '
    NR == FNR {
      target_cols[$1] = 1
      next
    }
    FNR == 1 {
      for (i = 1; i <= NF; i++) {
        if (target_cols[$i]) {
          keep_cols[i] = 1
          # 如需保留唯一列,取消下面一行注释
          # delete target_cols[$i]
        }
      }
      first = 1
      for (i = 1; i <= NF; i++) {
        if (keep_cols[i]) {
          printf "%s%s", first ? "" : " ", $i
          first = 0
        }
      }
      printf "\n"
      next
    }
    {
      first = 1
      for (i = 1; i <= NF; i++) {
        if (keep_cols[i]) {
          printf "%s%s", first ? "" : " ", $i
          first = 0
        }
      }
      printf "\n"
    }' "$subset_file" <(zcat majorfile.gz) | gzip > "$output_file"
done

关键优化点

  1. 用target_cols[$1] = 1替代计数逻辑,简化匹配判断
  2. 新增first标记控制空格输出,消除行尾多余空格
  3. 可选的delete target_cols[$i]实现唯一列保留
  4. 用<(zcat majorfile.gz)显式读取压缩文件,提升跨版本兼容性

内容的提问来源于stack exchange,提问作者M. Beausoleil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 18:25:39