Linux(二)shell使用 多命令连接符 转义符 常用特殊字
一 多命令执行的连接
共四个命令执行操作符,用于多命令同时执行时的连接符号。分别为:
操作符 | 说明 |
---|---|
| |
命令依照顺序执行 |
; |
忽略各个命令成功与否,从左到右依次执行 |
&& |
前一个命令执行成功后,执行后一个命令,否则不继续向下执行 |
|| |
前一个命令执行失败后,执行后一个命令,如果前一个命令成功,则不继续执行 |
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
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'
以a开头
-
$
。与上述^
对应的,是$
符。表示匹配行结尾是某个字符的所有行。依然是上面的文档,我们匹配结尾为cs
的(不是必须只匹配一个字符,可以匹配连续的)。做匹配时$
是放在的,比如cs$
注意,我们这里说的,是正则表达式里的$
,而非shell
中用于获取变量的$
。
cat ./ta.txt
cat ./ta.txt|grep 'cs$'
以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>)'
提取html内容
可以让输出变得更加简洁
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
为打印匹配结果。