管道和grep是什么

我们在用Shell的时候,会用到|,这个是管道,它可以把前一个命令显示出来的结果,直接交给后一个命令继续处理。

grep是用于找出包含指定模式的行。例如,当我们有一个observations.txt的文本,里面包含的数据是:

1
2
3
4
5
6
7
8
9
10
station date temp wind precip weather
Beijing 2026-08-01 31.2 3.5 0.0 CLEAR
Shanghai 2026-08-01 29.4 4.2 12.5 RAIN
Guangzhou 2026-08-01 33.1 2.8 5.0 RAIN
Beijing 2026-08-02 30.5 6.1 18.2 STORM
Shanghai 2026-08-02 28.7 3.9 0.0 CLOUDY
Guangzhou 2026-08-02 34.0 2.1 0.0 CLEAR
Beijing 2026-08-03 32.0 2.7 0.0 CLEAR
Shanghai 2026-08-03 27.9 5.5 22.1 STORM
Guangzhou 2026-08-03 32.8 3.0 8.4 RAIN

当我们用grep 'Beijing' observations.txt时,筛选出来的结果是:

1
2
3
Beijing 2026-08-01 31.2 3.5 0.0 CLEAR
Beijing 2026-08-02 30.5 6.1 18.2 STORM
Beijing 2026-08-03 32.0 2.7 0.0 CLEAR

可以看到我们将所有包含Beijing的行筛选了出来。

当我们用grep 'RAIN' observations.txt时,得到的结果是:

1
2
3
Shanghai 2026-08-01 29.4 4.2 12.5 RAIN
Guangzhou 2026-08-01 33.1 2.8 5.0 RAIN
Guangzhou 2026-08-03 32.8 3.0 8.4 RAIN

我们将所有包含RAIN筛选了出来。

所以这里我们就能知道了,grep的基本用法就是,从文本中找出包含指定字符串或者模式的行

grep 功能汇总

-i 忽略大小写
-n 显示行号
-v 反向匹配
-E 使用扩展正则表达式
-c 直接统计匹配行数
-r 递归搜索目录
-l 只显示匹配的文件名

下面展示几个示例。

grep忽略大小写

需要注意的是,grep是默认区分大小写的RAINrain默认是不同的,如果想忽略大小写,可以使用-i,例如grep -i 'rain' observations.txt

grep同时满足两个条件

如果想同时搜索包含BeijingRAIN的行,可以使用管道:grep 'Beijing' observations.txt | grep 'RAIN'。我们刚刚提到,管道是**“将前一个命令处理后的结果,交给后一个命令继续处理”**,所以我们现在的处理流程就是:

1
2
3
4
5
原始文件
↓ grep 'Beijing'
只剩包含 Beijing 的行
↓ grep 'RAIN'
只剩同时包含 RAIN 的行

grep显示行号

如果我们想显示grep输出内容对应的行号,我们使用grep -n 'STORM' observations.txt,我们会得到输出:

1
2
5:Beijing 2026-08-02 30.5 6.1 18.2 STORM
9:Shanghai 2026-08-03 27.9 5.5 22.1 STORM

grep 反向筛选

当我们使用grep -v 'CLEAR' observations.txt,得到的结果是排除所有晴天的记录,因为-v是保留不匹配CLEAR的行。另外,当我们使用grep -v '^station' observations.txt时,这里^代表行首,所以它不是排除所有包含station的行,而是排除以station开头的行

正则表达式

正则表达式需要与Shell通配符来共同学习区分,我们经常使用的Shell通常由Shell (Zsh) 来解释,而正则表达式通常由grepsedawk等程序解释。在使用正则表达式时,我们通常加上引号,这是因为,引号的作用是告诉Shell:不要处理这里面的特殊字符,把他们原样交给grep

我们接下来先来学习几个正则表达式的方法:

行首^和行尾$

我们通过这种方法可以来查找对应行首和行尾的行,例如查找以Beijing开头的行:

1
grep '^Beijing' observations.txt

查找以STORM结尾的行:

1
grep 'STORM$' observations.txt

同时限制行首和行尾:

1
grep -E '^Shanghai.*STORM$' observations.txt

.表示任意单个字符

在正则表达式中,.表示任意一个字符。例如我们在查找时:

1
grep '2026.08.01' observations.txt

它能够匹配2026-08-01

所以我们一般如果确实要匹配普通小数点,需要转义\.

例如筛选包含31.2的行:

1
grep '31\.2' observations.txt

如果我们不这样写,而是写成:

1
grep '31.2' observations.txt

可能会匹配到:31-231A2等。

*表示一个字符重复零次或者多次

举一个简单的例子:A*可以表示空字符串、A、AA、AAA...,而比较常用的.*可以表示任意字符出现零次或者多次

所以说:

1
grep -E '^Beijing.*STORM$' observations.txt

表示Beijing + 中间任意内容 + STORM

其余的我先不写了,使用的时候再查吧,因为懒的写了,这个实际上还是需要多用啊。

awk是什么

当我们需要按行来筛选的时候,使用grep是比较合适的,但当我们需要按列来筛选的话,使用awk就更加方便了。

我们先来拆解一下我们的文件内容,正如本篇博客开始的,我们的文件每一行有六列:

1
2
3
$1         $2          $3    $4    $5       $6
station date temp wind precip weather
Beijing 2026-08-01 31.2 3.5 0.0 CLEAR

我来记录几个比较实用的吧,全部的估计我也记不完,后面慢慢补充。

跳过标题行

awk中,NR表示当前记录的行号,如果我们要跳过标题行,打印需要的内容的话,可以类似这样写:awk 'NR > 1 {print $1, $3}' observations.txt,这表明,我们跳过第一行开始打印,并且打印第1列和第3列。

按数值筛选

找出温度超过32°C的记录:

1
awk 'NR > 1 && $3 > 32 {print $1, $2, $3}' observations.txt

需要注意的是,这里面NR > 1代表跳过标题,&& 代表并且,$3 > 32代表第三列温度大于32。

除此之外,我们还可以学一些新的逻辑方法:在awk中,= 表示赋值,== 表示判断是否相等,|| 表示或者,!= 表示不等于。

需要注意的是,awk应该使用单引号,不要使用双引号。而且,我们的一般写法是:awk '条件 {动作}' 文件,并且&&只能连接条件,不能连接条件和动作

我们举一个例子,计算总降水量:

1
awk 'NR > 1 {total += $5} END {print "总降水量:", total, "mm"}' observations.txt

这里NR > 1是条件,而{total += $5}是动作,用来计算的,awk会对每一行执行:total += $5,但在执行过程中,不会自动显示变量,文件读取完毕后,END才负责输出结果。

好,我们再总结一遍,在使用时,我们用**awk '条件 {动作}' 文件**,需要注意的是:

1
2
3
4
5
AWK 程序通常放在单引号里
字段用 $1、$2、$3 表示
多个输出字段之间使用逗号
&& 只能连接多个判断条件
END 用于最终统计输出

但是实际使用过程中,还是多用多查为好。