一 多命令执行的连接
共四个命令执行操作符,用于多命令同时执行时的连接符号。分别为:
| 操作符 | 说明 |
|---|---|
| |
命令依照顺序执行 |
; |
忽略各个命令成功与否,从左到右依次执行 |
&& |
前一个命令执行成功后,执行后一个命令,否则不继续向下执行 |
|| |
前一个命令执行失败后,执行后一个命令,如果前一个命令成功,则不继续执行 |
1.1 管道
上篇文章中,讲过 输入输出流 的概念,现在我们可以把每条运行的指令(实际是一个软件程序),想象为一个水管,每个水管都是左侧进水,右侧出水。
下图以echo abc为例,echo为一个水管,左侧为标准输入中流入的abc,经过echo后流入屏幕进行显示

管道的概念,是把很多水管连在一起,形成管道。比如两个命令 echo abc 和 grep a如下图:

echo接收abc作为参数,之后输出;此时因为后面接续着一个水管,数据流自然是流到下一个水管中,也就是echo的
abc,成为了grep的。
grep指令的作用,是在输入中,找到可以与参数相匹配的内容,并显示匹配行。所以在屏幕上可以显示出abc的结果,实际指令的执行情况如下图

再比如
ls | wc -l
将ls 的输出,输入到 wc(word count)后,可以统计ls所列出的项目数量。

管道小结: 以管道连接的指令,将会由左向右顺序执行;并且,左侧指令执行的输出,将作为右侧指令执行的输入。
1.2 “与”连接
&& 连接的指令,左侧指令执行失败后,则右侧指令不再执行。相当于逻辑中的and(与)关系。以下指令中,文件tv_none.txt不存在。
echo abc && ls tv_none.txt && ifconfig
执行效果如下图,可以看到,当 ls指令报错后,ifconfig指令并未运行

1.3 “或”连接
; 无论左侧指令是否执行成功,均执行右侧指令。相当于逻辑中的or(或)关系。依然是上面的三条指令,改为使用;连接。
echo abc; ls tv_none.txt; ifconfig
运行效果如下图,可以看到一共部分输出,其中第二部分是报错信息。

1.3 “非”连接
||只有当左侧指令报错时,右侧指令才会执行。如果左侧指令成功,则右侧指令不再执行。相当于逻辑关系中的Not(非)。依然是上面的三条指令,需要把ls改为第一个执行,也就是放在最左侧,之后使用|| 连接
ls tv_none.txt||echo abc||ifconfig
运行效果如下图,可以看到ls指令报错后,依然执行了echo指令,但echo指令成功执行后,并未继续执行ifconfig指令。

二 转义符
常用的转义符包括:\(反斜杠) ''(单引号) ""(双引号)
2.1 \
反斜杠,用于将特殊字符转化为普通字符,例如当我们键入Enter时,其实是向shell传递了一个换行符,shell在接收到换行符后开始执行指令,但是如果在命令的末尾加入\,则指令不会被执行,而是继续等待输入。
ls \
效果如下图,此时如果继续输入 -l,之后 键入Enter,则程序执行成功,与直接执行ls -l一样。


2.2 '' 与 ""
''可以将 特殊字符转化为普通字符;
""可以将特殊字符转化为普通字符。依然是上面的指令,使用
''包裹\,与使用""包裹''的效果是不同的。
ls '\'
ls "\"
可以看到,当使用''包裹时,ls指令会把\当作要列出的对象;而使用""包裹时,与2.1中相同,依然保持了\的特性。

三 常用特殊字符
除了上述转义符、操作符外、重定向外,还有一些常用的特殊字符。
3.1 $符
用于获取变量的值。变量及常量的概念,在前述文章中已经讲过。bash的编程能力在前述文章中也曾提到。既然bash是可以编程的,那么对变量的支持则是理所当然的。在bash中,我们使用变量名=变量值的格式去声明一个变量,使用$变量名去获取这个变量的值。如下代码:
file_name="test.txt"
cat $file_name # $file_name是file_name的值
在代码中$file_name是file_name的值,cat $file_name 等效于cat test.txt。

再比如,可以通过$直接获取到环境变量
echo $PATH

注意:
声明后的变量,仅在本shell中有效。
3.2 反单引号 `
反单引号,位于键盘左侧,从上向下的第二个键,英文状态下可直接输入。
两个反单引号之间应该包裹一个指令,被包裹的指令将被执行,执行结果将作为字符串被输出。看示例:
echo ls -l
echo `ls -l`
可以看到,当 echo后直接跟 ls -l时,ls -l直接作为一个字符串的输入,被echo输出到屏幕上。但当ls -l被反单引号包裹之后,echo输出到屏幕上的,成了ls -l执行后的结果。但很明显,和直接执行ls -l时,格式是不一样的,echo在进行输出的时候,将空白字符(包括换行符),替换成了空格。

四 正则表达式
4.1 正则表达式的基本用法
正则表达式,是用来在一段内容中,做高级搜索和匹配的工具,与普通的搜索不同,正则表达式是一种模式搜索。
需要说明的是,正则表达式并不是Linux系统特有的东西,它其实是很通用的,可以在很多编程语言、搜索工具中都可以使用的。
首先可以回顾一下普通的搜索,当我们在文本文件中,需要搜索某个内容时,我们按下Ctrl + F组合键,然后输入我们要搜索的内容,则可以进行搜索。

这里我加红了"具体"一词,是因为在普通搜索中,你必须要搜索很确切的东西,而不是某些更笼统的,比如还是上面的文字,我想要搜索出所有的数字。普通搜索是无法做到的,这时候,就需要用到正则表达式。
sudo cat /mnt/share/mountain_tai.txt | grep '[0-9]*'

因为要完成复杂的模式匹配,所以需要约定一些用法,也就是正则表达式的语法、关键字等。下面就常用的用法分步进行介绍。
-
^。如果想找到行开头都有某个字符的所有行,那么我们需要用到^(英文模式下,组合键Shift + 6)。比如我们想在文件中,搜索行开头有a的所有行,就可以用^a
cat ./ta.txt #查看ta.txt 的内容,包括三行,其中两行以a开头
cat ./ta.txt|grep '^a'

-
$。与上述^对应的,是$符。表示匹配行结尾是某个字符的所有行。依然是上面的文档,我们匹配结尾为cs的(不是必须只匹配一个字符,可以匹配连续的)。做匹配时$是放在的,比如
cs$
注意,我们这里说的,是正则表达式里的$,而非shell中用于获取变量的$。
cat ./ta.txt
cat ./ta.txt|grep 'cs$'

-
{}。想匹配某个字符多次,则可以使用{}.比如想找出连续两个s,可以用s{2}.
注意,grep直接执行复杂正则时,有些符号需要转义,可以用grep -E或者egrep
cat ./ta.txt
cat ./ta.txt|grep 's{2}' #此句无任何输出
cat ./ta.txt|egrep 's{2}'
cat ./ta.txt|grep 's\{2\}'
cat ./ta.txt|grep -E 's{2}'

如果只是想匹配两次,直接使用两个重复字符也可以。比如此处使用grep 'ss'也是一样的效果
- 用
{m,n},还可以匹配m到n次,或者至少n次
cat ./ta.txt
cat ./ta.txt|egrep 's{2,3}'
cat ./ta.txt|egrep 's{2,}'
cat ./ta.txt|egrep 's{2}'

6.[]。 在正则开始时,我们匹配了数字,使用的[]符号,使用[],就是在搜索结果中,有[]内的任意内容,都将进行匹配。上述的数字匹配,一位的数字均为0到9。而使用+,则表示要匹配。所以
[0-9]+则表示要匹配的内容里,必须包含一位或多位的0到9数字。
cat ./ta.txt
cat ./ta.txt|egrep '[0-9]+'

4.2 正则表达式的综合使用例子
- 一个经常使用的,是进行日期格式的匹配,比如当你要求用户输入的日期格式,必须是
YYYY-MM-DD时,则可以使用正则表达式来匹配。
年四位,月和日必须使用两位数,中间以-间隔:
cat ./ta.txt|egrep '[0-9]{4}-[0-1]{1}[0-9]{1}-[0-3]{1}[0-9]{1}'
在组织正则表达式时,需要严谨的逻辑,对要匹配模式的严谨总结,否则容易出现纰漏。如上述规则,其实并不能完全规范正确的日期

日期其实是要分情况的,不考虑的过于复杂。当十位是0-2时个位可以是0-9;当十位是3时,个位只能是0-1。此时需要分组进行匹配,用到(组一|组二|组n|)使用括号包括,用竖线分割,只要有一组被满足,则视为满足条件。
下面的表达式,可以过滤掉日期为37的数据。但依然不是严谨的日期匹配。
cat ./ta.txt|egrep '[0-9]{4}-[0-1]{1}[0-9]{1}-([0-2]{1}[0-9]{1}|[3]{1}[0-1]{1})'

- 对于爬虫来讲,需要对爬取的网页信息做提取。网页都是有规律的标签,此时正则表达式则更加好用。比如如下网页代码,是一份脱敏后的网页表格数据。
<div class="column_search_result">
<div class="content">
<table width="100%" border="0" cellspacing="0" cellpadding="0" id="data_table_2" class="data_table">
<tbody>
<tr>
<th style="width: 5%; border-left: 1px solid #CFCFCF;">区域</th>
<th style="width: 12%;">项目名称</th>
<th style="width: 10%;">项目坐落</th>
<th style="width: 10%;">预售证号</th>
<th style="width: 17%;">开发企业</th>
<th style="width: 5%;">批准时间</th>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">发区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('8177','3104854')">庭二期</a> </td>
<td align="center">街以南,规路以东</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-08-22</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">发区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7331','3104853')">麓项目</a> </td>
<td align="center">东街以北,天平路以西</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-08-22</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">岳区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7452','3101320')">度假区一期住宅</a> </td>
<td align="center">环路以北、路以东</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司(山东)有限公司</td>
<td align="center">2023-08-15</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7448','3090592')">山玖玺</a> </td>
<td align="center">路以南、路以东</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-07-26</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7430','3089444')">著小区</a> </td>
<td align="center">岱街以北,路以东</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-07-24</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">岳区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7460','3086713')">墅一期项目</a> </td>
<td align="center">路以南、路以东、市体育中心以北</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-07-19</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: rgb(249, 245, 237);">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">岳区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7460','3082774')">墅一期项目</a> </td>
<td align="center">路以南、路以东、市体育中心以北</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-07-12</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">新区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7412','3082033')">府</a> </td>
<td align="center">北天街以南、路以西</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-07-11</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7445','3082602')">产业城项目西区</a> </td>
<td align="center">碧霞路以东、省电力学校以南</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司(泰安)有限公司</td>
<td align="center">2023-07-11</td>
</tr>
<tr onmouseover="this.style.background='#f9f5ed'" onmouseout="this.style.background='white'" style="background: white;">
<td style="border-left: 1px solid #CFCFCF;"><div align="center">区</div></td>
<td align="center"><a href="http://192.168.1.1:8000/reisPub/pub/preSaleBuildingStatist#" onclick="projectInfo('7448','3082603')">玖玺</a> </td>
<td align="center">路以南、路以东</td>
<td align="center">房预(销)xxxxx号</td>
<td align="center">xxxx公司</td>
<td align="center">2023-07-11</td>
</tr>
</tbody>
</table>
</div>
</div>
想提取出上述代码,列表中的数据,使用正则表达式可以很简单的实现
sudo cat /mnt/share/house_info.html|egrep '(center"><a .*>(.*)</a>|<td align="center">[^<a](.*)</td>)'

可以让输出变得更加简洁
sudo cat /mnt/share/house_info.html|sed -nr 's/<td align="center">[^<a](.*)<\/td>/\1/gp'
改用sed命令的替换模式,sed 's/要替换的内容/替换成的内容/选项'在此处,要替换的内容使用正则表达式,<td align="center">[^<a](.*)<\/td>,替换成的内容为\1表示正则表达式匹配的第一组内容,最后选项为gp其中g为全局匹配,p为打印匹配结果。
