给定一个包含以下内容的文件:
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
始终对应于相同的NAME
和COMPANY
。
如果情况并非如此,那么您必须采取稍微不同的做法:
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] }'
这会从输入中删除标题,对文件进行排序ID
,YEAR
然后使用新列重新插入标题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
可能会不是工作。