【问题标题】:Bash How to wrap values of the first line of a csv file with quotations, if they do not existBash如何用引号包裹csv文件第一行的值,如果它们不存在
【发布时间】:2018-02-27 11:29:03
【问题描述】:

前几天我问如何将 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 

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

【问题讨论】:

    标签: bash csv if-statement quotations


    【解决方案1】:

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

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

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

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

    【讨论】:

    • Tom 这个命令最适合我。如果值缺少引号并且其中一些包含引号,则会跳过它们,因此第一行如word1,"word2","word3","word4",word5word1,word2,word3,word4,word5 总是变为"word1","word2","word3","word4","word5"。你们非常有帮助。我当然感谢所有的帮助!
    • 唉,如果引用的字段有逗号,则会失败。例如。 one,"two,two",three 变为 "one","two","two","three"
    • 你是对的,@Heath,可能还有许多其他输入会导致它失败,毕竟这只是一系列不支持上下文的替换。处理 CSV 文件的最佳方法是使用支持它们的工具,例如带有 csv module 的 Python。
    • 有趣的是,使用 Python 的 csv 模块(来自 shell 脚本)确实是我最终做的事情。我会添加我的答案。
    【解决方案2】:

    由于行/列处理,这个问题比sed更适合awk:

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

    【讨论】:

    • 这个效果很好。我什至将引号放在随机值上,如下所示:word1,word2,"word3",word4,"word5",最终输出总是"word1","word2","word3","word4","word5"
    【解决方案3】:

    带有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,以便所有字段都被引用。它被提供一个从 stdin 读取的 csv 阅读器。
    • head -1 将第一行发送给 python 解释器进行处理。
    • ; tail -n +2 等待处理完成,然后从第二行开始转储每一行。

    【讨论】:

    • 很好,我唯一想说的是,当你使用它时应该引用"$QUOTE_CSV_PY"(它在这里没有讨厌的副作用,但可以与其他脚本一起使用)。
    • 完全正确。我在抄写时错误地删除了引号。固定。
    【解决方案4】:

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

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

    【讨论】:

    • 这很接近。如果这些值已经有引号,那么除了最后一个在结束引号之前有一个空格的值之外,所有内容都保持不变,如下所示:"word1","word2","word3","word4","word5 "
    【解决方案5】:

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

    文件.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  
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2015-06-18
      • 2019-02-14
      • 2020-07-05
      • 2019-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多