我使用gawk 4.0.1,我知道如何替换CSV文件中的列值,例如:
> ROW='1,2,3,4,5,6'
> echo $ROW | gawk -F, -vOFS=, '$2="X"'
1,X,3,4,5,6
但是,我正在处理一个包含分隔符的字符串的文件。读取列很顺利,但在替换值时,会插入一个额外的分隔符:
> ROW='1,"2,3",4,5,6'
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '{print $2}'
"2,3"
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '$2="X"'
1,X,,4,5,6
这就是我的期望:
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '$2="X"'
1,X,4,5,6
价值"" 2,3"'被替换为' X,'。我该如何解决这个问题?
编辑:我没有说明我也有空字段。因此,更好的行示例是:
ROW='1,,"2,3",4,5,6'
编辑2:从Dawg's回答我认为纯粹的awk是不可能的。虽然我同意使用python的解决方案更好,但awk的唯一解决方案是包含一些预处理和后处理来处理空字段。
#/bin/bash
ROW='1,,"2,3",4,"",5'
for col in {1..6}; do
echo $ROW |\
sed 's:,,:, ,:' |\
gawk -v c=$col -v OFS=, -v FPAT='([^,]+)|("[^\"]*")' '$c="X"' |\
sed 's:, ,:,,:g'
done
输出:
X,,"2,3",4,"",5
1,X,"2,3",4,"",5
1,,X,4,"",5
1,,"2,3",X,"",5
1,,"2,3",4,X,5
1,,"2,3",4,"",X
答案 0 :(得分:2)
$ echo $ROW | awk -vOFS=, -vFPAT="([^,]+)|(\"[^\"]+\")" '$2="X"'
1,X,4,5,6
我使用了GNU Awk Manual 4.7 Defining Fields By Content
中的模式与同一模式中的*
比较:
$ echo $ROW | awk -vOFS=, -vFPAT="([^,]*)|(\"[^\"]*\")" '$2="X"'
1,X,,4,5,6
所以答案是 - (对于这个有限的例子) - 使用-vFPAT="([^,]+)|(\"[^\"]+\")"
,但那对1,"2,3",4,,"","should be 6th field"
以下是两种空字段(,,
和""
)的结果:
$ echo $ROW2 | awk -vOFS=, -vFPAT="([^,]+)|(\"[^\"]+\")" '$2="X"'
1,X,4,"","should be 6th field"
^^ - missing the ',,' field
^^^ - now the 5th field -- BUG!
按照惯例,ROW2
应被视为包含6个字段,其中空白字段,,
和""
均计为1字段。如果不将空白字段计为字段,则将忽略空白后哪个字段的计数。使用awk正则表达式添加到CSV复杂的列表中。
知道CSV为surprisingly complicated并且处理多种可能性仅not trivial with awk或regex。
CSV的另一个解决方案是使用Perl或Python以及他们可以使用的更复杂和标准化的CSV库。对于Python,它是Python标准发行版的一部分。
这是一个与RFC 4180
完全兼容的Python解决方案$ echo $ROW | python -c '
> import csv, fileinput
> for line in csv.reader(fileinput.input()):
> print ",".join(e if i!=1 else "X" for i, e in enumerate(line))'
1,X,4,5,6
这样可以轻松处理更复杂的CSV。
以下是4个记录的5个字段CSV,其中引用字段为CRLF
,引用字段中包含转义引号,以及两种空白字段(,,
和{{1 }})。
""
使用相同的脚本(使用1,"2,3",4,5,6
"11,12",13,14,15,16
21,"22,
23",24,25,"26
27"
31,,"33\"not 32\"","",35
查看完整的字段值,但在正常情况下可能会使用repr
)根据RFC 4180正确处理所有这些情况:
str
使用awk很困难,因为$ cat /tmp/3.csv | python -c '
import csv, fileinput
for line in csv.reader(fileinput.input()):
print ",".join(repr(e) if i!=1 else "X" for i, e in enumerate(line))'
'1',X,'4','5','6'
'11,12',X,'14','15','16'
'21',X,'24','25','26\n27'
'31',X,'33\\not 32\\""','','35'
定义了每条记录,我们没有正确处理空字段,也没有正确处理转义引号:
\n
现在您需要将RS重新定义为在CR周围找到引号并使用awk读取多个行的正则表达式...添加对转义引号的支持...执行更复杂的正则表达式来拆分字段...复杂..祝你好运!
答案 1 :(得分:0)
输出是
$ ROW='1,"2,3",4,5,6'
$ echo $ROW | gawk -vOFS=, -vFPAT='[^,]+|"[^"].*"' '$2="X"'
1,X,4,5,6
这两个命令都运行正常。在第二个命令中,*
在粘贴时错过了。
perl的:
$var='1,"2,3",4,5,6';
$var=~s/\".*\"/X/g;
print $var;
答案 2 :(得分:0)
。应该在[^“]之后
这2个答案产生1,x,4,5,6的输出为ROW ='1,“2,3”,4,5,6'