Shell之数据处理

1. 背景

当有一些数据需要我们处理时,比如查找、替换、排序、求和等,我们可能会用Excel软件进行处理。在数据量较小的情况下,这是一种可行的方法,但当数据量比较大时,比如对大小1G的文件,如果还用Excel打开,则它可能挂掉了。这时我们可能会通过编写程序进行处理,比如用R或Python等,这对一些有编程基础的人而言,只是小菜一碟,但对没有编程基础的人而言,则不是那么简单,这时Shell登场了,虽然Shell也是一门编程语言,但完成一般的数据处理只需用几个简单的命令就可以了。

2. Shell简介

Shell是Unix/Linux系统中的脚本编程语言,它经常用于系统管理工作、数据和文本处理。使用Shell脚本主要有以下三个优点:

  • 简单。可以通过几个简单的命令就完成复杂的数据操作
  • 可移植。使用POSIX所定义的功能,可以做到脚本无须修改就可在不同的系统上执行
  • 开发容易。可以在短时间内完成一个功能强大又好用的脚本

2.1 如何使用Shell

如果是Linux系统或Mac OS X系统,则可以直接在终端使用Shell,如果是Windows系统,可以通过安装Babun使用Shell。

3. 使用Shell进行数据处理

这里我们通过构造一个数据样本来说明如何使用Shell进行数据处理,数据样本如下所示:

店铺id 商品id 商品价格 商品销量
10001 1 49 101
10001 2 59 200
10001 3 29 500
10002 1 45 150
10002 3 49 450
10002 6 69 322

该数据样本有6条记录,每一行代表一条记录,有四个字段,分别是店铺id,商品id,商品价格和商品销量。可以知道,店铺10001销售3件商品,分别是1,2,3,而店铺10002也销售3件商品,分别是1,3,6。将该数据样本保存在一个文本文件sample_data中,字段与字段间用空格隔开。

3.1 显示

当拿到一份数据的时候,我们一般会用软件打开,看看数据的格式,但是当数据很大的时候,这时用软件打开,它可能挂掉了。在Shell中,用于显示文件常用到的命令有catheadtail等,其中cat命令用于显示整个文件,而head命令和tail命令则用于显示文件的头部和尾部。

cat命令的基本格式如下:

cat [选项] [文件]

行为模式:显示文件内容,其中选项用于控制显示的方式

比如

1
$ cat sample_data

则会输出整个文件内容,如下:

1
2
3
4
5
6
10001 1 49 101
10001 2 59 200
10001 3 29 500
10002 1 45 150
10002 3 49 450
10002 6 69 322

而通过添加选项-n,则可以显示行号

1
$ cat -n sample_data

输出

1
2
3
4
5
6
1 10001 1 49 101
2 10001 2 59 200
3 10001 3 29 500
4 10002 1 45 150
5 10002 3 49 450
6 10002 6 69 322

但在一般情况下,我们只想看看数据样例,而不用查看整个文件,这时就可以用head和tail了。
head命令的基本格式如下:

head [选项] [文件]

行为模式:显示文件内容,其中选项用于控制显示的方式

比如想要查看文件的前3行,则可以使用命令

1
$ head -3 sample_data

输出

1
2
3
10001 1 49 101
10001 2 59 200
10001 3 29 500

tail命令用于显示文件的尾部,跟head命令用法类似。

3.2 查找

在Shell中,我们使用grep命令进行查找,grep命令的基本格式如下:

1
$ grep [选项] 模式 [文件]

行为模式:读取文件,显示匹配模式的行(记录),其中选项用于控制匹配和输出的方式

下面举一些例子说明grep是如何进行查找的:

A. 查找id为10001的记录

1
$ grep "10001" sample_data

输出

1
2
3
10001 1 49.9 101
10001 2 59 200
10001 3 29 500

B. 查找id为1的记录

1
$ grep -w "1" sample_data

输出

1
2
10001 1 49.9 101
10002 1 45 150

注意,我们在这里添加了选项-w,表示匹配整个字符串,而不是字符串的一部分,如果不输出字符串,则会将6条记录都输出,因为每条记录都含有”1”这个字符。

C. 查找id为10001的记录,输出匹配的部分,而非整行,使用-o选项

1
$ grep -o "10001" sample_data

输出

1
2
3
10001
10001
10001

D. 查找 id 为 1 的记录,并打印出匹配记录的行号,使用 -n 选项

1
$ grep -w -n "1" sample_data

输出

1
2
1:10001 1 49.9 101
4:10002 1 45 150

因此可以知道,在sample_data文件中,第1行和第4行有id为1的记录

grep常见的一些可用选项总结如下:

选项 说明
-w 搜索整个字符串
-n 打印匹配行的行号
-o 仅打印出匹配的一段,而非整行
-v 显示不匹配模式的行
-r 递归地查找目录及所有子目录的文件
-i 模式匹配时忽略字母大小写

3.3 排序

在Shell中,我们使用sort命令对数据进行排序。sort命令的基本格式如下:

1
$ sort [选项] [文件]

行为模式:将文件进行排序,并将排序结果写至标准输出,其中选项用于控制排序的方式

下面举一些例子说明sort是如何排序的:

A. 对商品价格进行排序,可以用-k选项指定要排序的字段,由于商品价格在第3个字段(由1开始计数),因此可以使用命令

1
$ sort -k3,3 sample_data

输出

1
2
3
4
5
6
10001 3 29 500
10002 1 45 150
10001 1 49 101
10002 3 49 450
10001 2 59 200
10002 6 69 322

这里要说明的是,如果仅指定一个字段编号,则排序键值会自该字段的起始处开始,一直继续到记录的结尾(而非字段的结尾)。而如果给的是一对用逗点隔开的字段数字,则排序键值将由第一个字段值的起始处开始,结束于第二个字段值的结尾。

因此,要对第3个字段进行排序,最好使用上述的命令,避免仅使用一个字段编号的格式:

1
$ sort -k 3 sample_data

sort默认是由小到大排序,如果想由大到小排序,可以使用-r选项。因此想要对商品价格由大到小排序,可以使用命令:

1
$ sort -k3,3 -r sample_data

B. 先以商品价格由小到大排序,再以商品销量由大到小排序,也就是会先将商品价格由小到大排序,然后对价格相同的商品,将它们的销量由大到小排序。这时可以通过指定多个-k选项完成:

1
$ sort -k3,3 -k4,4nr sample_data

输出

1
2
3
4
5
6
10001 3 29 500
10002 1 45 150
10002 3 49 450
10001 1 49 101
10001 2 59 200
10002 6 69 322

说明:这里的-n选项表明以整数类型比较字段。

3.4 其他

在Shell中,还有很多命令进行数据处理,比如cut命令,可以从输入文件中选择一或多个字段或者一组字符。

1
$ cut -d " " -f 3 sample_data

表示用空格做分割符(用-d指定),提取第3个字段(用-f指定),也就是输出第3列。

比如wc命令,它可以用来统计行数、字数和字节数等。

1
$ wc -l sample_data

会输出文件的行数。

另外,更高级的文件处理可以采用 sedawk

4. 参考资料