我目前分析的日志, 每行如下:
date,ip,text,source,custom-data,...
$1 ,$2,$3 ,$4 ,$5 ,...
一般情况下以,
分割处理即可;
但当 text
部分有,
时, 日志会在这个字段前后加上引号, 处理流程就会变成如下:
date,ip,"text A, text B",source,custom-data,...
$1 ,$2,$3 ,$4 ,$5 ,$6 ,...
这样后面的就全部乱套了, 当我想要取出$3
时, 就会取得一个"text A
这种东西..
所以请教一下大家, 这种情况下怎样处理比较好呢?
补充一下我目前暂时的解决方案, 使用 sed 将引号内的内容清除掉, 包括,
, 从而可以正确计数.
sed 's/".*"//g' logfile
但这样的局限也是显而易见的, 内容被更改了, 结果就不够准确.
awk
难以处理这样的逻辑
如果你使用GNU awk
可以尝试'FPAT'
或者使用python/perl
等脚本的csv模块
进行格式化处理
样本
$ echo -e '1,2,"3,3,3",4,5\n6,7,8,9,0'
1,2,"3,3,3",4,5
6,7,8,9,0
pythoncsv模块
处理,并以tab制表符
进行域分隔,再输出
$ echo -e '1,2,"3,3,3",4,5\n6,7,8,9,0' | python -c "import csv, sys; print '\n'.join(['\t'.join(row) for row in csv.reader(sys.stdin)])"
1 2 3,3,3 4 5
6 7 8 9 0
awk以tab
作为域分隔符进行处理
$ echo -e '1,2,"3,3,3",4,5\n6,7,8,9,0' | python -c "import csv, sys; print '\n'.join(['\t'.join(row) for row in csv.reader(sys.stdin)])" | awk '{print $1,$3,$5}'
1 3,3,3 5
6 8 0
当然,如果字符串中也可能有tab
会造成影响的话,可以将分隔符替换成\1
等不会造成冲突的非打印字符
$ echo -e '1,2,"3,3,3",4,5\n6,7,8,9,0' | python -c "import csv, sys; print '\n'.join(['\1'.join(row) for row in csv.reader(sys.stdin)])" | awk -F'\1' '{print $1,$3,$5}'
1 3,3,3 5
6 8 0
纯 awk 的解决方案,parse_csv
函数通过第一个返回一个 awk 数组,包含分隔好的 CSV 字段,直接用下标取值使用就可以了。注意,awk 的数组下标从 1 开始。
# parse csv files to an awk array.
awk -F, '{
parse_csv(r);
# replace following line with your code.
print r[1], r[2], r[3], r[4];
} function parse_csv(r, _quote, _i, _n) {
_i = 1;
_quote = 0; # in a quoted string or not.
for (_n = 1; _n <= NF; _n++) {
if (_quote) {
# quote string meets its end.
if (substr($_n, length($_n), 1) == "\"") {
_quote = 0;
r[_i] = r[_i]","substr($_n, 1, length($_n) - 1);
_i++;
} else {
# concat quote string with ",".
r[_i] = r[_i]","$_n;
}
} else {
# it is a quote string.
if (substr($_n, 1, 1) == "\"") {
_quote = 1;
r[_i] = substr($_n, 2);
} else {
# copy content for normal string without quote.
r[_i] = $_n;
_i++;
}
}
}
}'
https://gist.github.com/huandu/869fe48d6e3795875bfb