Bash如何用引号将csv文件第一行的值(如果不存在)包装起来
前几天我问如何用引号包装csv文件第一行的值。我得到的答复非常有效Bash如何用引号将csv文件第一行的值(如果不存在)包装起来,bash,csv,if-statement,quotations,Bash,Csv,If Statement,Quotations,前几天我问如何用引号包装csv文件第一行的值。我得到的答复非常有效 $ cat file.csv word1,word2,word3,word4,word5 12345,12346,12347,12348,12349 要仅在第一行中的项目周围加引号,请执行以下操作: $ sed '1 { s/^/"/; s/,/","/g; s/$/"/ }' file.csv "word1","word2","word3","word4","word5" 12345,12346,1234
$ cat file.csv
word1,word2,word3,word4,word5
12345,12346,12347,12348,12349
要仅在第一行中的项目周围加引号,请执行以下操作:
$ sed '1 { s/^/"/; s/,/","/g; s/$/"/ }' file.csv
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
我现在需要测试值周围是否存在引号,以消除重复引用值的可能性 更改每个替换以包括可选引号:
sed -E '1 { s/^"?/"/; s/"?,"?/","/g; s/"?$/"/ }' file.csv
我添加了-E
以启用扩展模式,因此?
被理解为“0或1匹配”
您还可以继续使用基本模式(无
-E
),并将每个?
替换为{0,1\}
(再次,0或1匹配)或*
(匹配0或更多)。由于行/列处理,此问题更适合awk而不是sed
:
awk 'BEGIN{FS=OFS=","} NR==1 {
for (i=1; i<=NF; i++) {gsub(/^"|"$/, "", $i); $i = "\"" $i "\""}
} 1' file
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
awk'BEGIN{FS=OFS=“,”}NR==1{
对于(i=1;i保留现有的sed命令,首先删除所有可能的双引号:
sed '1 { s/"//g; s/^/"/; s/,/","/g; s/$/"/ }' file.csv
为了测试每个答案,我创建了三个文件:
文件.csv
word1,word2,word3,word4,word5
12345,12346,12347,12348,12349
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
"word1",word2,word3,"word4",word5
12345,12346,12347,12348,12349
file2.csv
word1,word2,word3,word4,word5
12345,12346,12347,12348,12349
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
"word1",word2,word3,"word4",word5
12345,12346,12347,12348,12349
file3.csv
word1,word2,word3,word4,word5
12345,12346,12347,12348,12349
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
"word1",word2,word3,"word4",word5
12345,12346,12347,12348,12349
然后我创建了一个bash脚本
#!/bin/bash
sed -E '1 { s/^"?/"/; s/"?,"?/","/g; s/"?$/"/ }' file.csv > final.csv
sed -E '1 { s/^"?/"/; s/"?,"?/","/g; s/"?$/"/ }' file2.csv > final2.csv
sed -E '1 { s/^"?/"/; s/"?,"?/","/g; s/"?$/"/ }' file3.csv > final3.csv
然后我看了最后的文件,第一行很完美
# cat final*.csv
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
"word1","word2","word3","word4","word5"
12345,12346,12347,12348,12349
带有sed
和awk
的正则表达式可能会遇到一系列似乎永无止境的失败边缘情况。相反,利用csv库可以提供更强大的健壮性
我发现Python的库是最好的选择,因为它:
除了Python本身之外,广泛可用,没有繁重的依赖关系
对您使用的Python版本不特别敏感
有助于嵌入shell脚本;以及
非常紧凑(一个班轮就可以了!)
因此,我的解决方案大致如下:
QUOTE_CSV_PY='import sys; import csv; csv.writer(sys.stdout, quoting=csv.QUOTE_ALL).writerows(csv.reader(sys.stdin))'
head -1 file.csv | python -c "$QUOTE_CSV_PY"; tail -n +2 file.csv
要分解它:
QUOTE\u CSV\u PY
是一个shell变量,包含Python one-liner命令
- Python命令只需导入标准的
sys
和csv
模块。然后,它创建一个csv编写器,使用QUOTE\u ALL
集将数据写入stdout
,以便所有字段都被引用。它被输入一个从stdin
读取的csv读取器
head-1
将第一行发送给python解释器进行处理
;tail-n+2
等待处理完成,然后从第二行开始转储每一行
这很接近。如果这些值已经有引号,除了最后一个值在引号前有空格外,其他值都保持不变,如:“word1”、“word2”、“word3”、“word4”、“word5”
这一个非常有效。我甚至将引号放在随机值中,如:word1、word2、“word3”、“word4”、“word5”
最终输出总是“word1”、“word2”、“word3”、“word4”、“word5”
Tom此命令最适合我。如果值缺少引号,并且其中一些值有引号,则跳过它们,因此第一行如word1、“word2”、“word3”、“word4”,word5
或word1,word2,word3,word4,word5
总是变成“word1”,“word2”,“word3”,“word4”,“word5”
。你们帮了大忙。我当然非常感谢所有的帮助!唉,如果引用的字段有逗号,这就失败了。例如1,“2,2”,3
变成“1”,“2”,“2”,“2”,“3”
。你说得对,@Heath,可能还有许多其他输入会导致它失败,毕竟这只是一系列不了解上下文的替换。处理CSV文件的最佳方法是使用支持它们的工具,例如Python及其.fully,使用Python的CSV
模块(在shell脚本中)这确实是我最后要做的。我会添加我的答案。很好,我唯一要说的是,“$QUOTE\u CSV\u PY”
在使用它时应该被引用(它在这里没有令人讨厌的副作用,但可以与其他脚本一起使用)。完全正确。我在转录时错误地删除了引号。修复。