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

如何让Bash列重排脚本适配不同分隔符?

修正支持多分隔符的Bash列重排脚本

你的核心问题是分隔符未处理正则特殊字符(比如#是awk注释符,,是默认字段分隔符的一部分),加上可能的参数顺序错误,导致非TSV场景失效。以下是修正后的脚本及关键说明:

修正后的脚本

#!/bin/bash

# 参数校验:输入文件、输出文件、分隔符、discard选项、至少一个列名
if [ $# -lt 5 ]; then
    echo "用法: $0 <输入文件> <输出文件> <分隔符> <discard(yes/no)> <列名1> [列名2...]"
    exit 1
fi

# 解析参数
INPUT_FILE="$1"
OUTPUT_FILE="$2"
DELIMITER="$3"
DISCARD="$4"
shift 4
TARGET_COLS=("$@")

# 转义分隔符中的正则特殊字符(如# . * + ? ^ $等)
ESCAPED_DELIM=$(printf '%s' "$DELIMITER" | sed 's/[.*+?^$(){}|[\]\\]/\\&/g')

# 核心awk处理逻辑
awk -v raw_delim="$DELIMITER" -v regex_delim="$ESCAPED_DELIM" -v discard="$DISCARD" -v target_cols="${TARGET_COLS[*]}" '
BEGIN {
    FS = regex_delim  # 用转义后的分隔符做字段分割
    OFS = raw_delim   # 输出用原始分隔符,保证格式一致
    split(target_cols, col_list, / /)  # 将目标列名转为数组
    col_count = length(col_list)
}
# 处理表头:建立列名->列号的映射
NR == 1 {
    for (i = 1; i <= NF; i++) {
        name_to_idx[$i] = i
    }
    # 输出指定列
    for (i = 1; i <= col_count; i++) {
        printf "%s%s", (i>1?OFS:""), col_list[i]
    }
    # 不丢弃其余列时,追加未指定的列
    if (discard == "no") {
        for (i = 1; i <= NF; i++) {
            skip = 0
            for (j = 1; j <= col_count; j++) {
                if ($i == col_list[j]) {
                    skip = 1
                    break
                }
            }
            if (!skip) {
                printf "%s%s", OFS, $i
            }
        }
    }
    print ""
    next
}
# 处理数据行
{
    # 输出指定列
    for (i = 1; i <= col_count; i++) {
        printf "%s%s", (i>1?OFS:""), $(name_to_idx[col_list[i]])
    }
    # 不丢弃其余列时,追加未指定的列
    if (discard == "no") {
        for (i = 1; i <= NF; i++) {
            skip = 0
            for (j = 1; j <= col_count; j++) {
                if ($i == col_list[j]) {
                    skip = 1
                    break
                }
            }
            if (!skip) {
                printf "%s%s", OFS, $i
            }
        }
    }
    print ""
}
' "$INPUT_FILE" > "$OUTPUT_FILE"

关键修正点

  1. 分隔符转义:用sed处理分隔符中的正则特殊字符,避免awk把#当成注释、.当成任意字符,保证字段分割正确。
  2. 明确参数顺序:强制参数顺序为输入文件→输出文件→分隔符→discard选项→列名,避免参数错位。
  3. FS/OFS分离:FS用转义后的正则分隔符做字段分割,OFS用原始分隔符输出,保证输出格式和输入一致。
  4. 去重追加逻辑:处理非丢弃场景时,跳过已指定的列,避免重复输出。

针对你的示例的正确用法

注意分隔符需要用引号括起来,避免Shell解析错误:

bash arrange.sh hash.tsv hash_rearranged.tsv "#" no NUM1 CHR

执行后输出文件hash_rearranged.tsv内容:

NUM1#CHR#NUM2
9999999#chr22#10001000
10009999#chr22#10010100

内容的提问来源于stack exchange,提问作者sasa.asa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:55:57