Bash 如果 csv 文件第一行的值不存在,如何用引号引起来

Bash How to wrap values of the first line of a csv file with quotations, if they do not exist

前几天我问如何用引号将 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,12347,12348,12349 

我现在需要测试值周围是否存在引号以消除双引号值的可能性。

更改每个替换以包括可选引号:

sed -E '1 { s/^"?/"/; s/"?,"?/","/g; s/"?$/"/ }' file.csv

我添加了 -E 以启用扩展模式,因此 ? 可以理解为“0 或 1 个匹配”。

您也可以继续使用基本模式(没有 -E)并将每个 ? 替换为 \{0,1\}(同样,0 或 1 个匹配项)或 * (匹配 0 个或多个)。

这个问题比 sed 更适合 awk,因为 row/column 处理:

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
  • 使用 gsub 函数,我们删除前导或尾随双引号(如果存在)
  • 然后我们可以安全地将每个单元格用双引号括起来

通过先删除所有可能的双引号来保留现有的有效 sed 命令:

sed '1 { s/"//g; s/^/"/; s/,/","/g; s/$/"/ }' file.csv 

为了测试每个答案,我创建了三个文件:

file.csv

word1,word2,word3,word4,word5  
12345,12346,12347,12348,12349 

file2.csv

"word1","word2","word3","word4","word5"  
12345,12346,12347,12348,12349

file3.csv

"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  

带有 sedawk 的正则表达式会受到一系列看似永无止境的失败边缘情况的影响。相反,利用 csv 库提供了更多的稳健性。

我发现 Python 的图书馆是最佳选择,因为它:

  1. 除Python本身外,广泛可用,没有繁重的依赖关系;
  2. 对您使用的 Python 版本不是特别敏感;
  3. 适合嵌入到 shell 脚本中;和
  4. 非常紧凑(单线就可以!)。

因此,我的解决方案是:

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_CSV_PY 是一个包含 Python 单行命令的 shell 变量
  • Python 命令只导入标准的 syscsv 模块。然后它创建一个 csv 编写器,写入 stdout 并设置 QUOTE_ALL 以便所有字段都被引用。它被馈送一个 csv reader 从 stdin.
  • 读取
  • head -1 将第一行发送到 python 解释器进行处理。
  • ; tail -n +2 等到处理完成,然后转储从第二行开始的每一行。