我环顾四周,但我找不到任何已经回答此问题的人。
我正在编写一个bash脚本,它将读取6个不同的csv
个文件,并计算所有文件中有多少行一起包含某些标记。
(它是一个联系人列表数据库 - 还有商业或私人电子邮件地址的标签)
以下是我编写的代码示例:
### First Scan - Business emails ###
bus="$(awk 'BEGIN {FS = ","}{print $27}' FILE*full* | grep -c "Business")"
echo "No. of Business Accounts: $bus"
### Second Scan - Private emails ###
priv="$(awk 'BEGIN {FS = ","}{print $27}' FILE*full* | grep -c "Private")"
echo "No. of Private Accounts: $priv"
脚本返回看似完全正确的值。然而!我知道每个文件中的每一行都有标记' business'或者'私人'在相同的位置 - 并没有空行,但当我将两个结果加在一起时,它不等于完整的行数......大约有45000个缺失......
stdin
或grep
的{{1}}是否有任何限制 - 完整的数据库超过200万行......
请帮忙! : - )
答案 0 :(得分:5)
最有可能的是,数百万行csv中的一些包含带引号的带引号的字段。 Awk对引用一无所知;它会以逗号分开。
如果您正在使用Gnu awk,则可以使用FPAT
变量,该变量允许您为字段指定正则表达式,而不是字段分隔符的正则表达式。例如,这将适用于许多CSV文件(如果csv文件使用CR-LF行结尾,则除了行结束问题)。 (-v var=value
大致相当于BEGIN{var="value"}
,而不仅仅是Gnu awk。)
gawk -v FPAT='[^",][^,]*|("[^"]*")*'
顺便说一句,没有必要使用grep
和awk
。您可以使用awk过滤和计数;实际上,您可以在同一扫描中执行两个计数:
gawk -v FPAT='[^",][^,]*|("[^"]*")*' '
$27 ~ /Business/ {++bus}
$27 ~ /Private/ {++pri}
END { print "No. of Business accounts", bus
print "No. of Private accounts", pri}' FILE*full*
上面的正则表达式非常简单,它不会处理"错误的" CSV文件(如果您可以将这个词用于这种松散定义的格式)。匹配:
[^",][^,]*|("[^"]*")*
| | | | | | |
+----+--+-+--+--+-+----- A character other than quote or comma
| | | | | |
+--+-+--+--+-+----- Followed by any number of characters other than comma
| | | | |
+-+--+--+-+--- OR
| | | |
| | | +----- Any number of sequences consisting of
| | |
+--+--+--------- A quote
| |
+--+--------- Any number of characters other than a quote
|
+--------- Another quote
因此,第一个替代方案将匹配未加引号的字段,例如93.7
或Private
,第二个替代方案将匹配:
引用字段,可能包括逗号:"Blood, sweat and tears"
根据引用加倍规则引用带有内部引号的字段:"""My goodness,"" she said"
(请参阅RFC 4180的第2.7节。)
它没有尝试匹配反斜杠转义的引号,它们不是标准的一部分(它们也不是由MS Excel生成的,afaik),如果引用的字段错误地包含一个不加引用的引号,它将完全失败
您可以在上面的程序中使用一个简单的变体来查看未正确解析的行,这可能会让您修复它们,或者调整正则表达式,如:
gawk -v FPAT='[^",][^,]*|("[^"]*")*' '
$27 !~ /Business/ && $27 !~ /Private/ {
print "----"
print "Error at line " NR:
print $0
for (i=1; i<=NF; ++i) printf "%2d: |%s|\n", i, $i
}' filename
答案 1 :(得分:1)
试试这个并告诉我们你得到了什么输出:
awk -F',' '
$27 ~ /Business/ { bus++; next }
$27 ~ /Private/ { priv++; next }
{ other++; print "Non-Business/Private:", FILENAME, FNR, $27 }
END { print NR, bus, priv, other }
' FILE*full*
上面输出中的NR代表总记录,应该等于bus + priv + other。