【问题标题】:Bash: Cut a CSV File from specific Column NamesBash:从特定列名中剪切一个 CSV 文件
【发布时间】:2022-01-11 16:53:53
【问题描述】:

我得到了一个包含很多无用信息的 CSV 文件,我希望从该文件中获取我需要的信息到另一个 CSV 文件中。

当前状态:

First Name,Middle Name, Last Name, Title, Suffix, Nickname, Given Yomi, Surname Yomi....
Angel,,Romero,,,Romi,, ....

在新文件中应该是这样的:

First Name, Last Name, Nickname
Angel, Romero, Romi

我想通过使用诸如 cut 和列名之类的东西来做到这一点,而不仅仅是字段编号。 不知何故像这样:

cut -d',' -f"First Name" file

我知道这行不通,但还有其他方法吗?

【问题讨论】:

  • 因为不需要重新排序,cut -d ',' -f '1,3,6' 就足够了
  • 你问过the exact same question。请编辑您的原始问题,而不是打开一个新问题
  • “我想通过使用 cut 和列名之类的东西来做到这一点,而不仅仅是字段编号。”为什么?如果这确实是您想要的,请尝试 SQL。应该很容易导入任何小型数据库。
  • 来自csvkit 的csvcut 命令正是这样做的:csvcut -Sc 'First Name','Last Name' file.csv
  • @glennjackman 我试过了,但它总是告诉我“csvcut:找不到命令”,即使我安装了 csvkit

标签: bash csv cut


【解决方案1】:

工具是Miller:

mlr --csv cut -o -f "field A","field B" input.csv >output.csv

Herecut 动词的文档。

【讨论】:

  • 由于某种原因我无法安装该工具..
  • @MahmoudAbdulkarim 你的操作系统是什么?您在安装过程中遇到了什么错误?
【解决方案2】:
awk -v tags='First Name,Last Name,Nickname' '
    BEGIN {
        FS=", *"; OFS=", "
        numOutFlds = split(tags,outFldNr2tag)
    }
    NR==1 {
        for (inFldNr=1; inFldNr<=NF; inFldNr++) {
            tag = $inFldNr
            tag2inFldNr[tag] = inFldNr
        }
    }
    {
        for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
            tag = outFldNr2tag[outFldNr]
            inFldNr = tag2inFldNr[tag]
            val = $inFldNr
            printf "%s%s", val, (outFldNr<numOutFlds ? OFS : ORS)
        }
    }
' file
First Name, Last Name, Nickname
Angel, Romero, Romi

【讨论】:

    【解决方案3】:
    $ cat csvcut.awk
    # csvcut.awk
    
    function csvsplit(str, arr,     i,j,n,s,fs,qt) {
        # split comma-separated fields into arr; return number of fields in arr
        # fields surrounded by double-quotes may contain commas;
        #     doubled double-quotes represent a single embedded quote
        delete arr; s = "START"; n = 0; fs = ","; qt = "\""
        for (i = 1; i <= length(str); i++) {
            if (s == "START") {
                if (substr(str,i,1) == fs) { arr[++n] = "" }
                else if (substr(str,i,1) == qt) { j = i+1; s = "INQUOTES" }
                else { j = i; s = "INFIELD" } }
            else if (s == "INFIELD") {
                if (substr(str,i,1) == fs) {
                    arr[++n] = substr(str,j,i-j); j = 0; s = "START" } }
            else if (s == "INQUOTES") {
                if (substr(str,i,1) == qt) { s = "MAYBEDOUBLE" } }
            else if (s == "MAYBEDOUBLE") {
                if (substr(str,i,1) == fs) {
                    arr[++n] = substr(str,j,i-j-1)
                    gsub(qt qt, qt, arr[n]); j = 0; s = "START" } } }
        if (s == "INFIELD" || s == "INQUOTES") { arr[++n] = substr(str,j) }
        else if (s == "MAYBEDOUBLE") {
            arr[++n] = substr(str,j,length(str)-j); gsub(qt qt, qt, arr[n]) }
        else if (s == "START") { arr[++n] = "" }
        return n }
    
    BEGIN { # read and store output field names
        for (i=1; i<ARGC; i++) { fields[++nfields] = ARGV[i]; ARGV[i] = "" } }
    
    NR == 1 { # read and store input field names, write output header
        for (i=1; i<=csvsplit($0,arr); i++) { names[arr[i]] = i }
        for (i=1; i<=nfields; i++) { printf "%s%s", sep, fields[i]; sep = "," }
        printf "\n" }
    
    NR > 1 { # read input record, split fields, write output record
        delete csv; sep = ""; n = csvsplit($0, csv)
        for (i=1; i<=nfields; i++) {
            printf "%s%s", sep, csv[names[fields[i]]]; sep = "," }
        printf "\n" }
    $ cat mahmoud.input
    FirstName,MiddleName,LastName,Title,Suffix,Nickname,GivenYomi,SurnameYomi
    Angel,,Romero,,,Romi,,
    $ awk -f csvcut.awk FirstName LastName Nickname <mahmoud.input
    FirstName,LastName,Nickname
    Angel,Romero,Romi
    

    【讨论】:

      【解决方案4】:

      鉴于您有一个没有变量空间的直接 CSV,您可以直接使用 Ruby 的 csv 解析器(无需先清理 csv 文件...)

      给定:

      cat file
      First Name,Middle Name,Last Name,Title,Suffix,Nickname,Given Yomi,Surname Yomi
      Angel,,Romero,,,Romi,,
      

      您可以过滤每个 csv 行:

      ruby -r CSV -e 'BEGIN{wanted=["First Name", "Last Name", "Nickname"]
                            puts wanted.to_csv
                            }     
      CSV.parse($<.read, headers:true).each{
          |h| puts h.to_hash.select{
          |k,v| wanted.include?(k) }.values.to_csv}' file
      

      打印:

      First Name,Last Name,Nickname
      Angel,Romero,Romi
      

      这里的优点是支持完整的 csv 文件,包括带有嵌入式分隔符的引用字段。

      【讨论】:

        【解决方案5】:

        可能晚了也不是很笼统,但如果你不需要重用脚本就很简单了:

        awk 'BEGIN {FS=", *"; OFS=","}{print $1,$3,$6}' input.csv &gt; output.csv

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-05
          • 2012-11-11
          • 1970-01-01
          • 2011-07-18
          • 1970-01-01
          • 2016-06-07
          • 2016-07-18
          • 2013-03-18
          相关资源
          最近更新 更多