【问题标题】:Add a column to a csv file using a bash script使用 bash 脚本将列添加到 csv 文件
【发布时间】:2021-06-04 20:35:24
【问题描述】:

我想在给定条件的情况下使用 bash 脚本将一列附加到 csv 文件。条件是 file1.csv 中的列必须有多个唯一值才能添加到 newfile.csv。这些不是真正的文件。原始文件有更多的列/行。

类似这样的:

file1.csv

1, ah, th, ab, a
2, ah, jk, ab, b
3, ah, lk, ab, c
4, ah, hh, ab, d

newfile.csv 应该是:

1, th, a
2, jk, b
3, lk, c
4, hh, d

这是我尝试过的脚本。但是,它不会追加新列。输出只是一个 csv,其中 file1.csv 的最后一列具有多个唯一值。

#!/bin/bash
cut -d, -f1 file1.csv > newfile.csv
limit=1
for i in $(seq 2 5); do
   value=$(cat file1.csv | cut -d, -f$i | uniq -u | wc -l)
   if [ $value -gt $limit ]; then
        paste -d, newfile.csv <(cut -d, -f$i file1.csv) > newfile.csv
   else echo "Column $i not appended."
   fi
done

我怀疑这可能与我在一行中有两次 newfile.csv 的事实有关。我尝试为每次交互创建一个新文件 newfile2.csv,但这不起作用。我是 Bash 新手。

【问题讨论】:

  • 每一行的列数是否相同?
  • 文件有多大,它们可以放入内存吗?

标签: bash csv for-loop awk append


【解决方案1】:

通过重命名脚本中的文件解决了问题:

#!/bin/bash
cut -d, -f1 file1.csv > newfile.csv
limit=1
for i in $(seq 2 5); do
   value=$(cat file1.csv | cut -d, -f$i | uniq -u | wc -l)
   if [ $value -gt $limit ]; then
        cut -d, -f$i file.csv > column.csv
        paste -d, newfile.csv column.csv > newfile2.csv
        cp newfile2.csv newfile.csv
   else echo "Column $i not appended."
   fi
done

【讨论】:

    【解决方案2】:

    在每个 Unix 机器上的任何 shell 中使用任何 awk,这将有效地工作并使用最少的内存:

    $ cat tst.awk
    BEGIN { FS=OFS=", " }
    NR==FNR {
        if ( NR == 1 ) {
            split($0,uniq)
        }
        for (inFldNr in uniq) {
            if ( seen[inFldNr,$inFldNr]++ ) {
                delete seen[inFldNr,$inFldNr]
                delete uniq[inFldNr]
            }
        }
        next
    }
    FNR==1 {
        for (inFldNr=1; inFldNr<=NF; inFldNr++) {
            if (inFldNr in uniq) {
                out2inFldNr[++numOutFlds] = inFldNr
            }
        }
    }
    {
        for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
            inFldNr = out2inFldNr[outFldNr]
            printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
        }
    }
    

    $ awk -f tst.awk file1.csv file1.csv
    1, th, a
    2, jk, b
    3, lk, c
    4, hh, d
    

    【讨论】:

      【解决方案3】:

      另一个类似的awk 双重扫描文件

      $ awk -v F', ' 'NR==FNR {for(i=1;c[i]<2 && i<=NF;i++) if(!f[i,$i]++) c[i]++; next}
                      FNR==1  {for(i=1;i<=NF;i++) if(c[i]>1) a[++k]=i}
                              {for(i=1;i<=k;i++) printf "%s%s",$(a[i]),i==k?ORS:FS}' file{,}
      
      1, th, a
      2, jk, b
      3, lk, c
      4, hh, d
      

      短路列已经有多个唯一值,打印时只扫描非唯一列

      file{,} 表示 file file,由于双重扫描算法,提供两次输入文件。

      【讨论】:

        【解决方案4】:

        您可以使用这 2 阶段 awk 解决方案:

        awk 'BEGIN {FS=OFS=", "} FNR==NR {for (i=1; i<=NF; ++i) if (!seen[i,$i]++) ++fq[i]; next} {s=""; for (i=1; i<=NF; ++i) if (fq[i] > 1) s = (s == "" ? "" : s OFS ) $i; print s}' file{,}
        
        1, th, a
        2, jk, b
        3, lk, c
        4, hh, d
        

        展开形式:

        awk 'BEGIN {
           FS = OFS = ", "
        }
        FNR == NR {
           for (i=1; i<=NF; ++i)
              if (!seen[i,$i]++)
                 ++fq[i]
              next
        }
        {
           s = ""
           for (i=1; i<=NF; ++i)
              if (fq[i] > 1)
                 s = (s == "" ? "" : s OFS ) $i
           print s
        }' file{,}
        

        【讨论】:

        • 您能否详细说明如何将其保存为新的 csv 文件?当我在真正的 csv 上尝试它时,它并没有删除只有一个唯一值的列...
        • 只需 &gt; outfileawk 命令的末尾将输出重定向到新文件。我在答案中显示了生成的输出。
        • 我就是这么做的。新文件仍然具有原始文件的所有列。
        • 这意味着您的实际输入与问题中显示的不同。如果您提供实际输入并显示您的预期输出,那么我可以跟踪。
        • 代码底部有:我应该运行 file.csv{,} 吗?看来我需要指定文件扩展名才能工作。
        猜你喜欢
        • 2012-03-19
        • 1970-01-01
        • 1970-01-01
        • 2019-10-24
        • 2022-01-20
        • 2018-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多