我有一个包含 3 列的 TSV 制表符分隔文件:
ID\tTEXT\tTYPE
打印TYPE
我做的专栏
cat /dataset.csv | awk -F $'\t' '{print $3}'
这些值是诸如 等值的枚举{CLASS_A,CLASS_B,CLASS_C}
。
当匹配枚举中的每个值时,我需要使用 AWK 的内联方式来计算列NF
的出现次数(?)以获得:TYPE
CLASS_A 1300
CLASS_B 450
CLASS_C 988
[更新]
根据下面的解决方案,我把这个脚本的最后一个版本放在这里
#!/bin/bash
COL=$1
FILE=$2
awk -v col="$COL" -F $'\t' ' {c[$col]++}
END{
for (i in c) printf("%s\t%s\n",i,c[i])
}' $FILE
计算第 3 列中行出现次数的用法是
$ ./count_cols.sh 3 /myfile.csv
答案1
无需使用cat
来读取文件。 AWK 完全能够读取它。
核心c[$3]++
语句应该获取每种类型的行数。
然后,最后,只需打印(作为制表符分隔值)所有计数:
#!/bin/bash
awk -F '\t' ' {c[$3]++}
END{
for (i in c) printf("%s\t%s\n",i,c[i])
}' dataset.csv
附加
鉴于OP的评论是:
对于带有引号的列,我遇到一些问题,例如
that doesn\'t mean that you\'re not worth remembering think of the people who need to know they need to know so you need to show....
In this case the parsing on \t will failure.
我必须回顾一下答案。我创建了这个文件:
$ cat dataset.csv
1233 that doesn\'t mean that you\'re not worth remembering think of the people who need to know they need to know so you need to show... CLASS_0
1234 here CLASS_A
1235 goes the values CLASS_B
1236 "that need counting" CLASS_B
1237 "\like \this" CLASS_B
1238 \or \this CLASS_C
1239 including spaces CLASS_B
1240 but not tabs CLASS_A
1241 which could not work CLASS_B
1242 finally CLASS_C
1243 this is CLASS_A
1244 over CLASS_B
1245 988 CLASS_C
该文件与脚本一起使用时会给出正确的结果:
$ ./script
CLASS_A 3
CLASS_B 6
CLASS_C 3
CLASS_0 1
这是正确的结果。
当然,文件
tabs
具有3 个字段的正确数量,并且- 变量在扩展时被正确引用并且不是大写的。
要测试文件是否符合第一个要求,您可以使用以下脚本:
#!/bin/bash
filetoread="$2"
<"$filetoread" tr -dc '\t\n' |
awk '(length!=2){printf("Error in line: %s, has %s tabs\n",NR,length)}'
awk -F '\t' '(NF!=3){printf("Error in line: %s, has %s fields\n",NR,NF)}' "$filetoread"
它检查每行是否有两个制表符,并且
字段数(如 awk 所示)实际上是三个。
添加几行测试线:
… …
1239 including spaces CLASS_B
1 but not tabs CLASS_A
2 but not \ttabs CLASS_A
1240 but not tabs CLASS_A
… …
并运行上面的脚本:
$ ./script 3 dataset.csv
Error in line: 8, has 4 tabs
Error in line: 8, has 5 fields
检测具有四个选项卡(添加了两个)的行 ID 1,并且不会被带有\t
.
至于变量的引用和使用,那就需要你自己去改进了。
答案2
像这样的东西可以完成这项工作:
awk -F'\t' '
$3=="CLASS_A" {a+=1}
$3=="CLASS_B" {b+=1}
$3=="CLASS_C" {c+=1}
END {
printf "%s %d\n%s %d\n%s %d", CLASS_A,a,CLASS_B,b,CLASS_C,c
}' /dataset.csv
答案3
我希望我正确理解第 3 列可以包含“CLASS_A”“CLASS_B”或“CLASS_C”?
然后
awk -F'\t' '
{ seen[$3]++ ;}
END { for (i in seen) {
printf "%s : %s\n",i,seen[i]
}
}
' /dataset.csv
应该做这件事吗?
请注意,“for (i in saw)”并不能保证它们以“正确的顺序”读取,但您可以添加一个| sort
(在整个 awk 后面)对它们进行排序,或者使用更多复杂的技巧(在 awk 内) 。
如果您还需要跳过第一行(例如,如果它包含标题)添加awk 脚本第一行之前:
( NR==1 ) { next ;}
或按如下方式更改第一行:
( NR > 1 ) { seen[$3]++ ;}