在 bash 中解析类似 .csv 的文件
Parsing a .csv-like file in bash
我有一个格式如下的文件:
string1,string2,string3,...
...
我必须分析第二列,计算每个字符串的出现次数,并生成格式如下的文件:
"number of occurrences of x",x
"number of occurrences of y",y
...
我设法编写了以下脚本,效果很好:
#!/bin/bash
> output
regExp='^\s*([0-9]+) (.+)$'
while IFS= read -r line
do
if [[ "$line" =~ $regExp ]]
then
printf "${BASH_REMATCH[1]},${BASH_REMATCH[2]}\n" >> output
fi
done <<< "`gawk -F , '!/^$/ {print }' | sort | uniq -c`"
我的问题是:
有更好更简单的方法来完成这项工作吗?
特别是我不知道如何解决这个问题:
gawk -F , '!/^$/ {print }' miocsv.csv | sort | uniq -c | gawk '{print ","}'
问题是 string2 可以包含空格,如果是这样,对 gawk 的第二次调用将截断该字符串。
我也不知道如何打印所有字段 "from 2 to NF",保留分隔符,这可能会连续出现几次。
非常感谢,
再见
编辑:
根据要求,这里有一些示例数据:
(练习题,请见谅)
输入:
*,*,*
test, test ,test
prova, * , prova
test,test,test
prova, prova ,prova
leonardo,da vinci,leonardo
in,o u t ,pr
, spaces ,
, spaces ,
leonardo,da vinci,leonardo
leonardo,da vinci,leonardo
leonardo,da vinci,leonardo
in,o u t ,pr
test, test ,test
, tabs ,
, tabs ,
po,po,po
po,po,po
po,po,po
prova, * , prova
prova, * , prova
*,*,*
*,*,*
*,*,*
, spaces ,
, tabs ,
输出:
3, *
4,*
4,da vinci
2,o u t
3,po
1, prova
3, spaces
3, tabs
1,test
2, test
你可以制作你最后的 awk:
gawk '{ sub(" *","",[=10=]); sub(" ",",",[=10=]); print }'
或者使用 sed 来做这类事情:
sed 's/ *\([0-9]*\) /,/'
awk 中的一行代码:
awk -F, 'x[]++ { } END { for (i in x) print x[i] "," i }' input.csv
它将每个第 2 列字符串的计数存储在关联数组 x
中,最后循环遍历数组并打印结果。
要获得您为此示例显示的确切输出,您需要将其通过管道传输到 sort(1)
,将字段分隔符设置为 ,
,将排序键设置为第二个字段:
awk -F, 'x[]++ { } END { for (i in x) print x[i] "," i }' input.csv | sort -t, -k2,2
当然,唯一的条件是每行的第二列不包含 ,
这是一个 Perl 单行代码,类似于 Filipe 的 awk 解决方案:
perl -F, -lane '$x{$F[1]}++; END{ for $i (sort keys %x) { print "$x{$i},$i" } }' input.csv
输出根据第二列的字母顺序排序。
@F
自动拆分数组从索引 $F[0]
开始,而 awk 字段以
开始
我有一个格式如下的文件:
string1,string2,string3,...
...
我必须分析第二列,计算每个字符串的出现次数,并生成格式如下的文件:
"number of occurrences of x",x
"number of occurrences of y",y
...
我设法编写了以下脚本,效果很好:
#!/bin/bash
> output
regExp='^\s*([0-9]+) (.+)$'
while IFS= read -r line
do
if [[ "$line" =~ $regExp ]]
then
printf "${BASH_REMATCH[1]},${BASH_REMATCH[2]}\n" >> output
fi
done <<< "`gawk -F , '!/^$/ {print }' | sort | uniq -c`"
我的问题是: 有更好更简单的方法来完成这项工作吗?
特别是我不知道如何解决这个问题:
gawk -F , '!/^$/ {print }' miocsv.csv | sort | uniq -c | gawk '{print ","}'
问题是 string2 可以包含空格,如果是这样,对 gawk 的第二次调用将截断该字符串。 我也不知道如何打印所有字段 "from 2 to NF",保留分隔符,这可能会连续出现几次。
非常感谢, 再见
编辑:
根据要求,这里有一些示例数据:
(练习题,请见谅)
输入:
*,*,*
test, test ,test
prova, * , prova
test,test,test
prova, prova ,prova
leonardo,da vinci,leonardo
in,o u t ,pr
, spaces ,
, spaces ,
leonardo,da vinci,leonardo
leonardo,da vinci,leonardo
leonardo,da vinci,leonardo
in,o u t ,pr
test, test ,test
, tabs ,
, tabs ,
po,po,po
po,po,po
po,po,po
prova, * , prova
prova, * , prova
*,*,*
*,*,*
*,*,*
, spaces ,
, tabs ,
输出:
3, *
4,*
4,da vinci
2,o u t
3,po
1, prova
3, spaces
3, tabs
1,test
2, test
你可以制作你最后的 awk:
gawk '{ sub(" *","",[=10=]); sub(" ",",",[=10=]); print }'
或者使用 sed 来做这类事情:
sed 's/ *\([0-9]*\) /,/'
awk 中的一行代码:
awk -F, 'x[]++ { } END { for (i in x) print x[i] "," i }' input.csv
它将每个第 2 列字符串的计数存储在关联数组 x
中,最后循环遍历数组并打印结果。
要获得您为此示例显示的确切输出,您需要将其通过管道传输到 sort(1)
,将字段分隔符设置为 ,
,将排序键设置为第二个字段:
awk -F, 'x[]++ { } END { for (i in x) print x[i] "," i }' input.csv | sort -t, -k2,2
当然,唯一的条件是每行的第二列不包含 ,
这是一个 Perl 单行代码,类似于 Filipe 的 awk 解决方案:
perl -F, -lane '$x{$F[1]}++; END{ for $i (sort keys %x) { print "$x{$i},$i" } }' input.csv
输出根据第二列的字母顺序排序。
@F
自动拆分数组从索引 $F[0]
开始,而 awk 字段以