按引用行

时间:2017-04-21 14:08:59

标签: bash shell sorting

我有以下名为data的文件(分隔符是空格,但为了清楚起见,我在这里写了标签):

a   b   c   d   e   f   g
a   c   d   f   e
21  18  32  31  35
b   a   f   e   d   g
12  22  21  28  32  33
...

从第二行开始,我希望通过将包含字母的行与文件的第一行(a b c d e f g)相匹配来排序每对行,并保留每对字母编号,这样结果是:

a   b   c   d   e   f   g
a   b   c   d   e   f   g
21  0   18  32  35  31  0
a   b   c   d   e   f   g
22  12  0   32  28  21  33
...

请注意,对于每对行,可能会丢失字母,例如在data示例中,第一对行中有两个缺少的字母,第二对中有一个缺少字母。这些字母在所需的输出中被赋值为零。

到目前为止,我在网站上找到了以下代码:

while read line; do 
  sorted=$(sort -g -- <<< "${line// /$'\n'}")
  printf -- "${sorted//$'\n'/ }\n"
done < data

但它只是按字母或数字顺序对每一行进行排序:

a   b   c   d   e   f   g
a   c   d   e   f
18  21  31  32  35
a   b   d   e   f   g
12  21  22  28  32  33
...

我有没有办法修改代码,以便通过匹配文件的第一行,并保留每对行中的字母对数字来实现?

2 个答案:

答案 0 :(得分:2)

Perl救援:

perl -wle '@h = split " ", <>;
           print "@h";
           until (eof) {
               ($cols, $vals) = (scalar <>, scalar <>);
               my %map;
               @map{ split " ", $cols } = split " ", $vals;
               print "@h";
               print join " ", map $_ // 0, @map{@h};
           }' -- data
  • -l处理输入和输出中的换行符
  • @h是“标题”的数组,即从第一行获取的列名。见split
  • %map是一个哈希表,它将列映射到值。
  • @map{ list }是哈希切片语法。它以相同的顺序返回与列表中的键对应的值。
  • //运算符用0替换undef(缺失值)。

答案 1 :(得分:0)

在awk中,评论道:

NR == 1 {
    # Store reference line in string for simple printing later
    ref_str = $0

    # Store reference line in array
    split($0, ref)

    # Number of elements in reference line
    nel = NF
    print
}

NR > 1 {
    # Read letters into array
    split($0, keys)
    getline

    # Create array with letter/number pairs for current line pair
    for (i = 1; i <= NF; ++i)
        cur_line[keys[i]] = $i

    print ref_str

    # Loop over elements of reference line
    # Insert output field separator, except before first field
    # Print value from current line, or 0 if value is not in current line
    for (i = 1; i <= nel; ++i)
        out = out (i > 1 ? OFS : "") (cur_line[ref[i]] ? cur_line[ref[i]] : 0)

    print out

    # Delete array for current line; gawk: delete(cur_line)
    split("", cur_line)

    # Reset output line
    out = ""
}

输出以空格分隔:

$ awk -f so.awk infile
a   b   c   d   e   f   g
a   b   c   d   e   f   g
21 0 18 32 35 31 0
a   b   c   d   e   f   g
22 12 0 32 28 21 33

但是,出于验证目的,我们可以将列排成一行:

$ awk -f so.awk infile | column -t
a   b   c   d   e   f   g
a   b   c   d   e   f   g
21  0   18  32  35  31  0
a   b   c   d   e   f   g
22  12  0   32  28  21  33

这是为了清晰而不是简洁而写的。它应该是POSIX awk符合的。

表示文件末尾没有相应数字行的字母行。