【问题标题】:Bash - Removing empty columns from .csv fileBash - 从 .csv 文件中删除空列
【发布时间】:2020-02-06 15:25:40
【问题描述】:

我有一个大的 .csv 文件,我必须删除其中的空列。空,我的意思是他们有一个标题,但列的其余部分不包含数据。

我编写了一个 Bash 脚本来尝试执行此操作,但遇到了一些问题。 代码如下:

#!/bin/bash

total="$(head -n 1 Reddit-cleaner.csv | grep -o ',' | wc -l)"
i=1
count=0
while [ $i -le $total ]; do
        cat Reddit-cleaner.csv | cut -d "," -f$i | while read CMD; do if [ -n CMD ]; then count=$count+1; fi; done
        if [ $count -eq 1 ]; then
                cut -d "," -f$i --complement <Reddit-cleaner.csv >Reddit-cleanerer.csv
        fi
        count=0
        i=$i+1
done

首先我找到列数,并将其存储起来。然后,当程序没有到达最后一列时,我逐列循环。嵌套的 while 循环检查列中的每一行是否为空,如果有多行不为空,则将所有其他列写入另一个文件。

我知道这个脚本存在一些问题。首先,count 修改发生在子 shell 中,因此 count 永远不会在父 shell 中修改。其次,每次脚本找到空列时,我正在写入的文件都会被覆盖。

所以我的问题是我该如何解决这个问题。我最初想要它,以便它根据计数逐列写入新文件,但也不知道如何完成。

编辑:人们要求提供示例输入和输出。

Sample input:
User, Date, Email, Administrator, Posts, Comments
a, 20201719, a@a.com, Yes, , 3
b, 20182817, b@b.com, No, , 4
c, 20191618, , No, , 4
d, 20190126, , No, , 2

Sample output:
User, Data, Email, Administrator, Comments
a, 20201719, a@a.com, Yes, 3
b, 20182817, b@b.com, No, 4
c, 20191618, , No, 4
d, 20190126, , No, 2

在示例输出中,除了标题(帖子)之外没有数据的列已被删除,而完全或部分填充的列仍然存在。

【问题讨论】:

  • 您能否将示例输入数据与预期输出相加。
  • 你可以使用 bash 调用 python 脚本并用 3 行来解决这个问题(加载 pandas csv / remove emptys / save csv)。您是否坚持使用 bash 解决方案,可能是因为您不能保证 python / pandas 可用?
  • 另外,请考虑@anubhava 的请求。如果您不给我们一种方法来验证我们对输入和输出应该是什么样子的想法,我们将无法帮助您
  • 顺便说一句,您应该通过shellcheck.net 运行您的脚本并阅读why-is-using-a-shell-loop-to-process-text-considered-bad-practice。发明 shell 以创建/销毁文件和进程以及对工具的顺序调用的人还发明了 awk 作为 shell 调用以操作文本的工具。

标签: bash csv awk


【解决方案1】:

我可能误解了这个问题(由于缺少示例输入和预期输出),但这应该很简单:

$ x="1,2,3,,4,field 5,,,six,7"
$ echo "${x//,+(,)/,}"
1,2,3,4,field 5,six,7

这需要启用 extglob 的 bash。否则,您可以使用外部调用sed

$ echo "1,2,3,,4,field 5,,,six,7" |sed 's/,,,*/,/g'
1,2,3,4,field 5,six,7

您的示例代码中有很多冗余。你真的应该考虑awk,因为它已经跟踪了当前字段数(如NF)和行数(如NR),所以你可以在每一行加上一个简单的total+=NF。折叠空字段后,awk 可以在您想要的字段编号上运行。

$ echo "1,2,3,,4,field 5,,,six,7" |awk -F ',+' '
  { printf "line %d has %d fields, the 6th of which is <%s>\n", NR, NF, $6 }'
line 1 has 7 fields, the 6th of which is <six>

这里用printf表示记录数(NR,当前行号),字段数(NF)和第六个字段的值($6,也可以为一个变量,例如$NF 是最后一个字段的值,因为awk 是单索引的)。

【讨论】:

  • 谢谢,我只是 bash 的新手,所以目前只习惯于 cut 和 grep 等命令。我以前从未听说过 awk,但现在你有提到过。
  • awk 是 posix/bash 程序员最好的朋友。我已经停止使用sed 进行简单替换,这让cut 感觉像是在使用安全剪刀。
【解决方案2】:

这实际上是 CSV 解析器的工作,但您可以使用此 awk 脚本来完成工作:

cat removeEmptyCellsCsv.awk

BEGIN {
   FS = OFS = ", "
}
NR == 1 {
   for (i=1; i<=NF; i++)
      e[i] = 1  # initially all cols are marked empty
   next
}
FNR == NR {
   for (i=1; i<=NF; i++)
      e[i] = e[i] && ($i == "")
   next
}
{
   s = ""
   for (i=1; i<=NF; i++)
      s = s (i==1 || e[i-1] ? "" : OFS) (e[i] ? "" : $i)
   print s
}

然后运行如下:

awk -f removeEmptyCellsCsv.awk file.csv{,}

使用有问题提供的样本数据,它将产生以下输出:

1, User, Date, Email, Administrator, Comments
2, a, 20201719, a@a.com, Yes, 3
3, b, 20182817, b@b.com, No, 4
4, c, 20191618, , No, 4
5, d, 20190126, , No, 2

请注意,Posts 列已被删除,因为它在每条记录中都是空的。

【讨论】:

    【解决方案3】:
    $ cat tst.awk
    BEGIN { FS=OFS="," }
    NR==FNR {
        if ( NR > 1 ) {
            for (i=1; i<=NF; i++) {
                if ( $i ~ /[^[:space:]]/ ) {
                    gotValues[i]
                }
            }
        }
        next
    }
    {
        c=0
        for (i=1; i<=NF; i++) {
            if (i in gotValues) {
                printf "%s%s", (c++ ? OFS : ""), $i
            }
        }
        print ""
    }
    
    $ awk -f tst.awk file file
    User, Date, Email, Administrator, Comments
    a, 20201719, a@a.com, Yes, 3
    b, 20182817, b@b.com, No, 4
    c, 20191618, , No, 4
    d, 20190126, , No, 2
    

    如果您需要使用比您的问题中更复杂的 CSV,另请参阅 What's the most robust way to efficiently parse CSV using awk?

    【讨论】:

      【解决方案4】:

      您可以使用 Miller (https://github.com/johnkerl/miller) 及其 remove-empty-columns 动词。

      +------+----------+---------+---------------+-------+----------+
      | User | Date     | Email   | Administrator | Posts | Comments |
      +------+----------+---------+---------------+-------+----------+
      | a    | 20201719 | a@a.com | Yes           | -     | 3        |
      | b    | 20182817 | b@b.com | No            | -     | 4        |
      | c    | 20191618 | -       | No            | -     | 4        |
      | d    | 20190126 | -       | No            | -     | 2        |
      +------+----------+---------+---------------+-------+----------+
      

      正在运行

      mlr --csv remove-empty-columns input.csv >output.csv
      

      你会有

      +------+----------+---------+---------------+----------+
      | User | Date     | Email   | Administrator | Comments |
      +------+----------+---------+---------------+----------+
      | a    | 20201719 | a@a.com | Yes           | 3        |
      | b    | 20182817 | b@b.com | No            | 4        |
      | c    | 20191618 | -       | No            | 4        |
      | d    | 20190126 | -       | No            | 2        |
      +------+----------+---------+---------------+----------+
      

      【讨论】:

        猜你喜欢
        • 2012-06-29
        • 2013-01-03
        • 1970-01-01
        • 2015-03-12
        • 2014-10-16
        • 2022-01-22
        • 1970-01-01
        • 2014-07-27
        • 1970-01-01
        相关资源
        最近更新 更多