[英]Bash - Removing empty columns from .csv file
我有一个很大的 .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
在示例输出中,除了标题 (Posts) 之外没有任何数据的列已被删除,而完全或部分填充的列仍然存在。
我可能误解了这个问题(由于它缺少示例输入和预期输出),但这应该很简单:
$ 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
是final 字段,因为awk
是单索引的)。
它实际上是 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
列已被删除,因为它在每条记录中都是空的。
$ 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
另请参阅使用 awk 有效解析 CSV 的最可靠方法是什么? 如果您需要使用比问题中更复杂的 CSV 文件。
您可以使用 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 |
+------+----------+---------+---------------+----------+
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.