AWK / KSH Shell 文本处理/按组计数

AWK / KSH Shell 文本处理/按组计数

给定一个包含以下内容的文件:

ID      NAME    COMPANY  YEAR
111     LUIS    TCS     2016
222     URSO    XYZ     2014
333     OMAR    ABC     2015
444     SANJIB  MABE    2012
111     LUIS    TCS     2015
444     SANJIB  MABE    2011
111     LUIS    TCS     2015
222     URSO    XYZ     2015
333     OMAR    ABC     2014

需要进行两种排序,首先按 ID 排序,然后按年份排序,到目前为止我已经成功做到了。接下来是添加一个额外的列,其中包含相同 ID 的每次出现的计数,如下所示。

ID      NAME    COMPANY  YEAR   Count
111     LUIS    TCS     2015    1
111     LUIS    TCS     2015    2
111     LUIS    TCS     2016    3
222     URSO    XYZ     2014    1
222     URSO    XYZ     2015    2
333     OMAR    ABC     2014    1
333     OMAR    ABC     2015    2
444     SANJIB  MABE    2011    1
444     SANJIB  MABE    2012    2

等等。

提前致谢。

答案1

或许:

< file sort | awk '{print $0 "\t" ++n[$1]}'

答案2

对提供的示例数据进行单一排序会按照与您请求的输出相同的顺序返回输出。假设标题行不包含在排序中。

sort datafile

下一步是计算我们看到相同 ID 填充第 5 列的次数。我们还替换标题以包含新的第 5 列。

我们希望从 awk 解析中跳过原始标题行。

awk 'BEGIN {print "ID\tNAME\tCOMPANY\tYEAR\tCOUNT"} NR>1 {id_count[$1]++; print $0 "\t" id_count[$1]}' datafile

如果数据文件的第一行是标头,则 awk 会忽略该行(要求行/记录号大于 1)。

只需将第一个命令通过管道传输到第二个命令即可将命令放在一起。在下一个实例中,我们假设标题行不包含在排序的输出中。

sort datafile | awk 'BEGIN {print "ID\tNAME\tCOMPANY\tYEAR\tCOUNT"}  {id_count[$1]++; print $0 "\t" id_count[$1]}'

排序命令本身包含所有字段,因此您最终会按列排序,将 ID 放在一起,然后是名称,然后是公司,然后是年份。 awk 命令打印一个新标题(或多或少复制原始标题)并附加 COUNT 列。

通过使用关联数组检查字段 1 (ID) 来解析数据中的其余行。每行按原样打印,带有一个选项卡,并显示第 1 列附加到其上的次数。

最终的打印语句一开始可能会令人困惑,但 awk 中的空格字符是连接符。 $0、制表符和变量值都将在输出中压缩在一起。另一种写法可能是:

printf "%s\t%d\n",$0,id_count[$1]

答案3

斯特凡·查泽拉斯的回答很好,但忽略了数据的标题。它还假设特定的ID始终对应于相同的NAMECOMPANY

如果情况并非如此,那么您必须采取稍微不同的做法:

sort -k1,1 -k4,4 file.in | awk '{ print $0, ++n[$1] }'

要使标题正确:

sed '1d' file.in |
sort -k1,1 -k4,4 |
awk -vOFS="\t" \
   'BEGIN { print "ID", "NAME", "COMPANY", "YEAR", "Count" }
          { print $0, ++n[$1] }'

这会从输入中删除标题,对文件进行排序IDYEAR然后使用新列重新插入标题Count。然后,它为每个输入增加一个计数器ID,并在每行输入的最后一列中输出该计数器的当前值:

ID      NAME    COMPANY YEAR    Count
111     LUIS    TCS     2015    1
111     LUIS    TCS     2015    2
111     LUIS    TCS     2016    3
222     URSO    XYZ     2014    1
222     URSO    XYZ     2015    2
333     OMAR    ABC     2014    1
333     OMAR    ABC     2015    2
444     SANJIB  MABE    2011    1
444     SANJIB  MABE    2012    2

如果您想让awk从文件中挑选出原始标头并仅添加标Count头而无需再次显式键入它,您也可以这样做:

sed -e '1w /tmp/header.txt' -e '1d' file.in |
sort -k1,1 -k4,4 |
awk -vOFS="\t" \
   'NR == 1 { getline h <"/tmp/header.txt"; print h, "Count" }
            { print $0, ++n[$1] }'

rm -f /tmp/header.txt

这使得将sed标头写入文件/tmp/header.txt。当读取第一条记录时awk,该文件被读入变量h并与新列一起输出Count。脚本的其余部分awk与以前一样工作并产生相同的输出。

/tmp/header.txt文件保证存在以供awk脚本读取,因为直到第一个输入记录到达时才会读取该文件。此时,sort已经消耗了所有输入,sed这意味着头文件必须已被写入。在块中读取它BEGIN可能会不是工作。

相关内容