如何重新排列文本文件中的列?

如何重新排列文本文件中的列?
Month   Name  Marks  
2016-10 Sam   58  
2016-09 Sam   77  
2016-10 John  64  
2016-09 John  47  
2016-10 Mark  71  
2016-09 Mark  38  
2016-10 Steve 83  
2016-09 Steve 39  

我从数据库中获取此数据,其中第一列中有月份,第三列中有第二列每个学生的标记。现在我想以这样的方式进行编辑,使其在第一列中包含名称,在第二列中包含 2016-10 的标记,然后在第三列中包含 2016-09 的标记。

答案1

假设您的输入数据位于名为“grades”的文件中,请尝试:

$ awk 'BEGIN{ PROCINFO["sorted_in"]="@ind_str_desc"} NR==1{next} {m[$1]; n[$2]; g[$2,$1]=$3} END{for (name in n) {printf "%s",name; for (month in m) printf " %s",g[name,month]; print""}}' grades | column -t
Steve  83  39
Sam    58  77
Mark   71  38
John   64  47

输出为每个学生一行,成绩按月份降序排列。

对于那些喜欢将代码分布在多行中的人:

gawk '
    BEGIN{ PROCINFO["sorted_in"]="@ind_str_desc"}
    NR==1{
        next
    }

    {
        m[$1]
        n[$2]
        g[$2,$1]=$3
    }

    END{
        for (name in n) {
            printf "%s",name
            for (month in m)
                printf " %s", g[name,month]
                print""
        }
    }

    ' grades | column -t

怎么运行的

  • BEGIN{ PROCINFO["sorted_in"]="@ind_str_desc"}

    这告诉 awk 我们希望数组按索引排序。这是 GNU 的一项功能。

  • NR==1{next}

    这告诉 awk 跳过第一行。如果您想为输出文件添加标头,我们可以在这里完成。

  • m[$1]

    这告诉 awk 在关联数组中添加当前月份的条目m。我们不需要分配一个值,因为我们只需使用它来跟踪输入中存在哪些月份。

  • n[$2]

    这告诉 awk 在关联数组中添加学生姓名的条目n。我们不需要分配一个值,因为我们只需使用它来跟踪输入中存在哪些月份。

  • g[$2,$1]=$3

    这会将成绩分配为关联数组中学生姓名、月份键下的值g

  • END{for (name in n) {printf "%s",name; for (month in m) printf " %s",g[name,month]; print""}}

    到达文件末尾后,我们打印出每个学生的所有姓名和成绩。

  • column -t

    这个可选步骤使输出变得漂亮。

答案2

如果您需要双月

sed '
    2~2{                                               #for even lines
        N                                              #attach next line
        s/\(\S\+ \)\(\S\+ \)[0-9]*\n\(\S\+\).*/\2\1\3/ #rearrange two line
    }
    1c\Name Month1 Month2                              #output new header
    ' file.data

或者双标

sed '
    1!N             #from second line attach next line 
    s/\S\+ //       #remove first field (2016-10)
    s/\n.* / /      #remove 2 fields in attached line
    t               #ommit 1st line
    s/$/1 Marks2/   #arrange header
    ' file.data

其他版本

echo 'Name Marks1 Marks2' ;\
paste -sd' \n' <(tail -n +2 file.data) |
cut -d' ' -f 2,3,6

答案3

一个有点粗略的例子:

在这里,您将变量设置m为要包含的日期及其顺序;作为逗号分隔的字符串。根据下面的例子,这将是:

m=2016-10,2016-09

这又给出:

Name 2016-10 2016-09

这要求名称是唯一的并且没有空格......

awk -v m=2016-10,2016-09 '
    NR==1{next}
    {
        # Set array x[name][month]=marks
        x[$2][$1]=$3
    }
    END {
        split(m, k, ",")
        printf "Name"
        for (v in k)
            printf "\t%s", k[v]
        for (e in x) {
            printf "\n%s", e
            for (v in k)
                printf "\t%s", x[e][k[v]]
        }
        print ""
    }
' data

示例输出:

Name    2016-10 2016-09
Steve   83  39
Mark    71  38
John    64  47
Sam 58  77

经过column -t

Name   2016-10  2016-09
Steve  83       39
Mark   71       38
John   64       47
Sam    58       77

如果这是一次性的事情,并且您的数据按照示例(已订购,仅两个月等),那么这也可以:

awk 'NR==1{next}NR%2{print $3;next}{printf "%s\t%s\t",$2,$3}' data

相关内容