在命令行处理 CSV 文件

由于这些年 Python 和机器学习的流行,一说到 CSV 文件,马上想到的工具是 pandas。pandas 很好,但是很多简单的处理,用一个更轻量的工具可以实现,而且几乎所有的 Unix/Linux 系统都附带了这工具(当然也包括 macOS)。这就是 awk。我这篇要谈的是 awk 的几个使用例子。

假设有一个文件 bangumi.csv 内容如下:

日期 番名 更新集数 播放(万)
周一 书店里的骷髅店员本田 第5话 242.6
周二 关于我转生变成史莱姆这档事 第6话 2438.7
周三 强风吹拂 第6话 207.6
周四 青春猪头少年不会梦到兔女郎学姐 第6话 1847.9
周五 魔法禁书目录-第三季 第5话 1291.9
周六 JOJO的奇妙冒险-黄金之风 第5话 851.7
周日 刀剑神域-Alicization 第5话 2170.1

以下的各个例子都是对这个文件的处理。

一、输出某些列

例1

只想要番名那一列,可以这样用。

awk '{print $2}' bangumi.csv

结果:

番名
书店里的骷髅店员本田
关于我转生变成史莱姆这档事
强风吹拂
青春猪头少年不会梦到兔女郎学姐
魔法禁书目录-第三季
JOJO的奇妙冒险-黄金之风
刀剑神域-Alicization

注意点:

  1. awk 默认用空格或 \t 来分隔列。列分隔符可以用 -F 选项来指定。
  2. 列从 1 开始计数,$1表示第一列,$NF表示最后一列, $(NF-1)表示倒数第二列,$0表示整行。

例2

想要番名和日期那两列,并且调换下顺序,用,分隔,并且标出行号。

awk '{print NR ". " $2 "," $1}' bangumi.csv

结果:

1. 番名,日期
2. 书店里的骷髅店员本田,周一
3. 关于我转生变成史莱姆这档事,周二
4. 强风吹拂,周三
5. 青春猪头少年不会梦到兔女郎学姐,周四
6. 魔法禁书目录-第三季,周五
7. JOJO的奇妙冒险-黄金之风,周六
8. 刀剑神域-Alicization,周日

注意点:

  1. 双引号内的内容会原样输出。
  2. 变量 NR 表示行号。

二、输出某些行

例3

例2 中的结果包含了表头,这里修正一下,把表头去掉,只留下内容。

awk 'NR > 1 {print NR - 1 ". " $2 "," $1}' bangumi.csv

结果:

1. 书店里的骷髅店员本田,周一
2. 关于我转生变成史莱姆这档事,周二
3. 强风吹拂,周三
4. 青春猪头少年不会梦到兔女郎学姐,周四
5. 魔法禁书目录-第三季,周五
6. JOJO的奇妙冒险-黄金之风,周六
7. 刀剑神域-Alicization,周日

注意点:

  1. 加在大扩号前面的是对行的过滤条件。
  2. 因为输出少了一行,NR - 1修正一下行号。

例4

输出更新到第5话的日期和番名。

awk '/第5话/ {print $1,$2}' bangumi.csv

结果:

周一 书店里的骷髅店员本田
周五 魔法禁书目录-第三季
周六 JOJO的奇妙冒险-黄金之风
周日 刀剑神域-Alicization

注意点:

  1. 行过滤条件可以用正则表达式
  2. 正则表达式要写在一对/中间

三、统计

例5

在例4 的基础上,多输出一个行号。

错误示范:如果用 NR 输出行号,结果会是

# awk '/第5话/ {print NR ". " $1,$2}' bangumi.csv 

2. 周一 书店里的骷髅店员本田
6. 周五 魔法禁书目录-第三季
7. 周六 JOJO的奇妙冒险-黄金之风
8. 周日 刀剑神域-Alicization

正确的做法是用一个变量来计数:

awk '/第5话/ {n+=1; print n ". " $1,$2}' bangumi.csv

结果:

1. 周一 书店里的骷髅店员本田
2. 周五 魔法禁书目录-第三季
3. 周六 JOJO的奇妙冒险-黄金之风
4. 周日 刀剑神域-Alicization

注意点:

  1. ; 分隔多个动作。
  2. 变量初始值默认为空,空值用来计算的话,当0来处理。

例6

让例5 中的行号,从零开始。

awk 'BEGIN {n=-1} /第5话/ {n+=1; print n ". " $1,$2}'  bangumi.csv

结果:

0. 周一 书店里的骷髅店员本田
1. 周五 魔法禁书目录-第三季
2. 周六 JOJO的奇妙冒险-黄金之风
3. 周日 刀剑神域-Alicization

注意点:

  1. BEGIN的内容只在最开始的时候执行一次。

例7

把所有番剧的播放量累加一下,附在最后。

awk '{if (NR>1) s+=$4; print $0} END{print "----------------"; print "总播放量: " s "万"}' bangumi.csv

结果:

日期 番名 更新集数 播放(万)
周一 书店里的骷髅店员本田 第5话 242.6
周二 关于我转生变成史莱姆这档事 第6话 2438.7
周三 强风吹拂 第6话 207.6
周四 青春猪头少年不会梦到兔女郎学姐 第6话 1847.9
周五 魔法禁书目录-第三季 第5话 1291.9
周六 JOJO的奇妙冒险-黄金之风 第5话 851.7
周日 刀剑神域-Alicization 第5话 2170.1
----------------
总播放量: 9050.5万

注意点:

  1. 利用if ()语句可以做细致的过滤。
  2. END的内容只在前面的动作执行完之后执行一次。

如果想更多得了解「变量」「动作」等概念,可以参考一下阮一峰的这篇博客 awk 入门教程

    原文作者:桐山少年
    原文地址: https://www.jianshu.com/p/c3b53ad460f8
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞