逗号分隔值 又名 CSV 是一种半结构化数据,使用逗号作为分隔符来分隔单词。 CSV 文件格式在数据专业人员中非常流行,因为他们必须处理大量 CSV 文件并对其进行处理以创建见解。 在本文中,我们将重点介绍如何在 Linux 中解析 Bash shell 脚本中的 CSV 文件。
在本文的大部分内容中,我将使用 awk
和 sed
用于 csv 解析的工具,而不是组合不同的命令,例如 grep
, cut
, tr
, 等等。
这 awk
实用程序降低了管道多个命令或编写带有逻辑的循环以获取数据的复杂性。 相反,您可以在 awk
做这项工作。
内容
1. 准备 CSV 文件进行处理
您的 CSV 文件可能是从数据库、API 生成的,或者您可能已经运行了一些命令并将输出转换为 CSV 格式的分隔符。 在任何情况下,您都必须先分析数据集,然后再在其上运行逻辑。
作为最佳实践,您应该在使用数据集之前对其进行清理。 为什么要清理数据集? 可能会有空单元格值或标题中没有正确格式、处理不需要的额外列等等的情况。
我正在使用以下 CSV 数据,我从中获取 卡格尔 用于演示目的。
Player_Id,Player_Name,DOB,Batting_Hand,Bowling_Skill,Country 1,SC Ganguly,8-Jul-72,Left_Hand,Right-arm medium, 2,BB McCullum,27-Sep-81,Right_Hand,Right-arm medium, 3,RT Ponting,19-Dec-74,Right_Hand,Right-arm medium, 4,DJ Hussey,15-Jul-77,Right_Hand,Right-arm offbreak,Australia 5,Mohammad Hafeez,17-Oct-80,,Right-arm offbreak,Pakistan 6,R Dravid,11-Jan-73,,Right-arm offbreak,India 7,W Jaffer,16-Feb-78,,Right-arm offbreak,India 8,V Kohli,5-Nov-88,,Right-arm medium,India 9,JH Kallis,16-Oct-75,,Right-arm fast-medium,South Africa 10,CL White,18-Aug-83,Right_Hand,Legbreak googly,Australia 11,MV Boucher,3-Dec-76,Right_Hand,Right-arm medium,South Africa 12,B Akhil,7-Oct-77,Right_Hand,Right-arm medium-fast,India 13,AA Noffke,30-Apr-77,Right_Hand,Right-arm fast-medium,Australia 14,P Kumar,2-Oct-86,Right_Hand,Right-arm medium,India 15,Z Khan,7-Oct-78,Right_Hand,Left-arm fast-medium,India
1.1。 替换空单元格
在某些情况下,CSV 文件在特定单元格中没有任何值。 看看下面的屏幕截图,列之间有一些空单元格。
我总是用“NA”或“No Value”替换它,所以不会有空单元格。 您可以使用以下 awk
片段用您想要的值替换任何空单元格。 在这种情况下,我将用“无值”替换空单元格。
awk 'BEGIN{FS=",";OFS=","} { for(i=1;i<=NF;i++) { if($i == ""){ $i="No Value" } } print }' ~/Downloads/Player.csv > player_cleaned.csv
这个片段的工作方式是我将字段分隔符和输出字段分隔符设置为逗号 (FS=",";OFS=","
)。 使用 for loop
遍历一行中的每个单元格,如果发现一个单元格为空 ($i == ""
) 然后将其替换为 "No value"
($i="No value"
)。 您必须将更改重定向到新文件。
推荐阅读:
- 用示例解释 Bash 重定向
1.2. 大写标题
CSV 文件可能有也可能没有标题。 但是如果有一个标题,我总是将它大写以获得更好的可读性。 您可以使用 awk
要么 sed
. 我会告诉你两种方式。
awk 'BEGIN{FS=",";OFS=","} { if(NR==1){ print toupper($0) } else { print } }' player.csv > player_cleaned.csv
在这里,我们正在检查该行是否是第一行 using(NR==1
) 并使用 toupper()
函数将其大写。 相同的片段可以写成单行。
awk 'NR==1{ print toupper($0) }NR>1' player.csv > player_cleaned.csv
使用 awk
,您必须再次将更改重定向到新文件。 相反,您可以使用 ‘sed
‘ 将更改直接修改到文件中。 这里 U
将大小写转换为大写。 如果要进行小写转换,请使用 L
.
$ sed -i -e '1 s/(.*)/U1/' player_cleaned.csv
$ cat player_cleaned.csv
1.3. 删除尾随逗号
您的 CSV 文件末尾可能有一个逗号。 要清除尾随逗号,您可以按照以下方法。
我特意在行中添加了一个尾随逗号 7 到 11 在我的数据文件中。
要删除所有尾随逗号,请运行以下命令 sed
命令:
$ sed -i 's/,$//' ~/Documents/player_cleaned.csv

现在我们完成了清洁部分。 您可能还需要几个步骤,但这取决于您的 CSV 文件的结构以及需要清理的内容。
2. 在终端中漂亮地打印 CSV 文件
如果您尝试在终端中显示 CSV 文件,那么有几个选项可以让您以表格格式打印文件,这将使您具有更好的可读性。
2.1。 列命令
第一种方法是使用 column
命令。 Column 命令接受一个设置为逗号的分隔符和一个分隔符来分割在下面的命令中设置为制表符的列。 您还可以设置自己的自定义分隔符。
$ cat player_cleaned.csv | column -s, -t $ column -s, -t player_cleaned.csv

2.2. CSV 查找命令
csvlook 是 csvkit 软件包附带的实用程序。 不需要像我们一样设置分隔符 column
命令。
$ cat player_cleaned.csv | csvlook
$ csvlook player_cleaned.csv

2.3. Python 漂亮的表
如果你有蟒蛇 漂亮的 模块安装,然后您可以运行以下单行并重定向 CSV 文件以生成表。
python -c "import sys,prettytable; print(prettytable.from_csv(sys.stdin))" < player_cleaned.csv
您还可以创建一个 别名 对于单行并将文件名作为参数传递。
$ alias ptable="python -c "import sys,prettytable; print(prettytable.from_csv(sys.stdin))""
$ ptable < player_cleaned.csv

3. 从 CSV 文件中获取数据
3.1。 打印行数和列数
要获取 CSV 文件中的列数,请运行以下命令。 这里的变量 NF
表示用逗号作为分隔符分割的字段数。
$ awk -F, 'END{print NF}' player_cleaned.csv
6
要获取行数,请运行以下命令。 这里的变量 NR
表示当前记录(即)每一行被视为一个记录。
$ awk -F, 'END{print NR}' player_cleaned.csv
16
要跳过第一行(标题)并计算行数,请运行以下命令。
$ awk -F, 'END{print NR-1}' player_cleaned.csv
15
3.2. 打印整个 CSV 文件
这很简单。 您可以使用 cat
要么 awk
打印整个 CSV 文件。
$ cat player_cleaned.csv
$ awk '{print}' player_cleaned.csv
3.3. 仅打印 CSV 文件的标题
单独打印标题将使您对 CSV 文件包含的数据类型有一个很好的概述。 您可以使用 head
要么 awk
命令单独抓取标题。
$ head -n 1 player_cleaned.csv
$ awk 'NR==1' player_cleaned.csv PLAYER_ID,PLAYER_NAME,DOB,BATTING HAND,BOWLING SKILL,COUNTRY
3.4. 排除标题行
要排除标题行并打印所有其他行,请使用 awk
命令。 awk 变量 NR > 1
将跳过第一行。
$ awk '(NR>1)' player_cleansed.csv

sed 也可用于排除第一行并打印所有其他行。 这 1d
flag 将删除第一行并将所有其他行打印到标准输出(终端)。
$ sed 1d < player_cleaned.csv

3.5. 打印特定列
我们可以使用列位置来打印整列。 有两种方法可以实现这一点。 第一种方法是使用 awk 第二种方法是使用 循环. awk 抓取列会简单得多。
默认情况下,awk 根据分隔符拆分行并将值存储在 $1
, $2
, $3
等。awk 的默认分隔符是 空白.
看看下面的片段,其中字段分隔符(FS=","
) 和输出字段分隔符(OFS=","
) 设置为逗号。 print 语句将打印第一列、第二列和第六列。
awk 'BEGIN{FS=",";OFS=","} { print $1,$2,$6 }' player_cleansed.csv
您也可以将上面的代码段写成单行代码。
awk 'BEGIN{FS=",";OFS=","}{print $1,$2,$6}' player_cleansed.csv

现在第二种方法是使用循环。
IFS="," while read -r -a fields do echo ${fields[0]},${fields[1]},${fields[5]} done < player_cleaned.csv
让我解释一下当你运行上面的代码片段时到底发生了什么。
- 我们将内部字段分隔符 IFS 设置为逗号。
- 使用 read 命令,我们创建了一个名为“fields”的数组并将输入文件重定向到
while loop
. - 对于每次迭代,它将逐行读取并将该行作为数组元素存储在“字段”中,因此您可以使用数组索引位置单独获取特定列。
笔记: 索引值从 0..N 开始
3.6. 打印符合条件的行
如果您希望打印符合特定条件的行,那么您可以使用 awk
. 让我们来看看几个场景。
要打印与列中的值匹配的所有行,请运行以下命令。 在这里,我尝试打印与第 6 列中的值“India”匹配的所有行。
$ awk -F , '$6 == "India"' player_cleaned.csv

要打印与某个值不匹配的所有行,请运行以下命令。 而不是一个 等式运算符,我们正在使用 不等于运算符.
$ awk -F , '$6 != "India"' player_cleaned.csv

您还可以使用逻辑 AND、逻辑 OR 运算符对多个列进行条件检查。 假设我想检查所有国家为“印度”且击球手为“Right_hand”的行。
这里, $4
指向第 4 列和 $6
指向第 6 列。 符号 &&
用作逻辑 AND 运算符来评估两个条件。
$ awk -F , '$4 == "Right_Hand" && $6 == "India"' player_cleaned.csv

如果您希望将标头与条件检查的结果一起包含,请使用以下命令。 首先我打印第一行使用 NR==1
,然后使用逻辑 AND 运算符运行条件检查来打印结果。
$ awk 'NR==1' player_cleaned.csv && awk -F , '$4 == "Right_Hand" && $6 == "India"' player_cleaned.csv
如果您希望打印或重定向输出,请在子外壳中运行整个命令,方法是用 括号.
$ (awk 'NR==1' player_cleaned.csv && awk -F , '$4 == "Right_Hand" && $6 == "India"' player_cleaned.csv) | column -t -s,

关于 Csvkit 的说明
到目前为止,我们在本文中看到的一切都简单明了。 但是当您的 CSV 文件具有复杂的结构时,使用上述方法进行解析就会变得乏味。 有一个实用程序叫做 CSVKIT,这是处理 CSV 文件的出色实用程序 bash.
csvkit 实用程序的问题是它默认安装在您的发行版中,您可能必须手动安装它。 在您的公司环境中,这可能是不可能的,因为安装外部软件包可能存在一些限制。 但是这个实用程序值得一提,我们将为它创建一个单独的详细文章。
结论
在本指南中,我们了解了如何使用 awk、sed 处理 CSV 文件。 您还可以使用其他实用程序,如 cut、grep、tr 等来获得所需的结果,但 awk 和 sed 将使您的生活更简单,并降低编写大量代码的复杂性。 如果您有任何反馈,请在评论部分提及,我们将很高兴收到您的来信。
类似阅读:
- Bash 脚本——使用 getopts 解析 Bash 脚本中的参数
- 如何使用 Linux 命令行工具解析和打印 JSON
awkBASHBash 脚本Bash 技巧Bash 教程CLI逗号分隔值命令行CSV学习 Shell 脚本解析 CSV 文件sedShell 脚本shell 脚本