我尝试过使用此问题的答案,但无法适应我的具体需求。
https://stackoverflow.com/questions/58005013/count-the-number-of-rows-per-column-in-bash-shell
我的目标是编写一个 bash 脚本来读取 csv 文件,并返回每列的计数。计数是包含数值(包括短划线、句点和冒号)的行数。
给定一个像这样的 csv 文件:
2012-01-01 12:01:01, 1,3.2,NA,9,P
2012-01-01 12:01:01, 1,3,5,9,P
2012-01-01 12:01:01, 1,Bad,5,9,P
2012-01-01 12:01:01, 1,3,5,9,P
2012-01-01 12:01:01, 1,3,NA,9.7,P
我期望我的结果是:
5,5,4,3,5,0
答案1
使用awk
:
awk -F, -v OFS=, '
{ gsub(/[:. -]/, ""); for(c=1; c<=NF; c++) if($c==$c+0) count[c]++ }
END{ for(i=1; i<c; i++) printf "%d%s", count[i], (i+1<c?OFS:ORS) }' infnile
输出:
5,5,4,3,5,0
答案2
有多种方法可以做到这一点。
perl -sF, -lane 'my $i;
$s[$i++] += /^\h*\d/ for @F;
}{print @s;
' -- -,=, yourfile.csv
GNU sed + rs + awk + 粘贴实用程序:
sed -E '
s/(^|,)[0-9][^,]*/\11/g
s/(^|,)[^0-9,]*/\10/g
' yourfile.csv |
rs -Tc, |
awk '{print gsub(/1/,"")}'|
paste -sd, -
awk -F, -v OFS=, '
{
for (i=1; i<=NF; i++)
s[i] += $i ~ /^ *[0-9]/
}
END {
for (i=1; i in s; i++)
printf "%s%s", s[i], \
(i+1 in s) ? OFS : ORS
}' yourfile.csv
答案3
您可以使用正则表达式测试每个逗号分隔的字段(例如,查看它是否包含十进制数字)。将其转换为元素指标并进行运行计数。
例如使用 perl:
$ perl -F, -MList::MoreUtils=pairwise -lne '
BEGIN{@c = ()}
@ind = map { scalar $_ =~ /\d/ } @F; @c = pairwise { $a + $b } @c, @ind
}{
print join ",", @c
' file
5,5,4,3,5,0
类似的方法在 awk 中应该很简单 - 您只需要显式地循环元素即可。
答案4
使用乐(以前称为 Perl_6)
raku -e 'my $a; for [Z] lines.map(*.subst(" ", :global).split(",")) {
$a.push: m:g/ [\d+ % <[-:.]>]+ /.elems }; say $a.join(",");'
输入示例:
2012-01-01 12:01:01, 1,3.2,NA,9,P
2012-01-01 12:01:01, 1,3,5,9,P
2012-01-01 12:01:01, 1,Bad,5,9,P
2012-01-01 12:01:01, 1,3,5,9,P
2012-01-01 12:01:01, 1,3,NA,9.7,P
示例输出:
5,5,4,3,5,0
上面是用 Raku(Perl 编程语言家族的成员)编写的答案。首先$a
声明一个标量。然后简单地lines
读入并处理内部空白,只需通过map
-ping 输入并使用删除空白subst
(什么都不替换)即可。然后每个元素(行)都在逗号split
上,
,从而将行分成列。
完成第一个lines.map(*.subst(" ", :global).split(","))
操作后,列和行与 Raku 的“zip”运算符交换[Z]
,该运算符连续拉出表示列表的元素(例如构成表的第一列的所有元素),并将它们组合起来,直到元素位于一个列表中的位置已耗尽。
然后,针对正则表达式测试每个列元素,该正则表达式寻求与由包含, or , or的自定义字符类\d
分隔(使用 Raku 的%
修饰量词)的数字匹配。每个列匹配都被布尔化为with ,并对元素求和,然后-ed 到标量上。最后返回结果。-
:
.
True
.so
elems
push
$a
say
两个注意问题:
除了我所展示的(TMTOWTDI)之外,当然还有另一种方法来处理内部空白,例如,使用 Raku 的
:s
“sigspace”正则表达式副词。可以使用较短的代码和/或不保存到 Raku 变量来处理输入文本。下面的示例代码(但是,按行返回计数):
raku -e 'for [Z] lines.map(*.subst(" ", :global).split(",")) {
say m:g/ [\d+ \% <[-:.]>]+ /.elems };'
示例输出:
5
5
4
3
5
0
https://docs.raku.org/language/regexes#Modified_quantifier:_%,_%%
https://raku.org