最近在读机械工业出版社的《shell脚本学习指南》,不得不说单纯看书中的语法真的很晦涩难懂,还是结合常见应用场景和案例对比来学习吧!毕竟学也是为了更好的用嘛。
另,这里把学习过程中遇到的一些常见、好用的记录下来,方便以后回顾。毕竟编程语言和框架那么多,程序员是不可能时时刻刻把所有的语法和代码写法记在脑子里滴,知道能靠什么工具实现比较重要。况且在shell命令使用过程中,还有--help来帮忙,能给我们提供比较详尽的介绍。
一、Bash Shell
二、正则表达式 RE
三、Shell命令——文本神器三兄弟 awk,grep,sed
四、其他Shell命令
五、shell例题
六、参考资料
一、Bash Shell
1.bash Shell中的元字符:
bash Shell本身不支持正则表达式,使用正则表达式的是Shell命令和工具,例如grep、awk、sed等。但是bash Shell可以使用正则表达式中的一些元字符实现通配功能。这些元字符在通配中的意义与正则表达式中的意义不完全一致,如下所示;
* 表示——任意位的任意字符,而与前面的字母无关。这里和正则表达式的*对比一下,正则表达式中,*往往用于表达前面一个普通字符的0次或者多次重复。由此可见Shell元字符和正则元字符的不同。
? 表示——一个任意字符^表示——取反[]表示——字母集合(与正则中一样)
{}表示——一组表达式的集合·command·相当于$(command),用于进行命令替换,command为shell命令。(注:·是tab上方的反撇号,在这里编辑出来长得和shell中不同)${variable}相当于$variable,用于进行变量替换,variable表示shell变量。一般来说${variable}和$variable是一样的,只是${variable]在表达变量名的界限时更清晰。除此之外,${variable}还可以用于剪切或取变量的局部等。$[]与$(())用于进行数学计算。
2.管道和重定向:
【用法差异】
管道 —— command 1 | command 2 | command 3 左边的命令有标准输入,右边的命令接受标准输出
它仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 stdandard error 信息没有直接处理能力。然后,传递给下一个命令,作为标准的输入 standard input.。
重定向 ——
command1 < file1 左边的命令需要标准输入,右边只能是文件
command1 > file2 左边的命令有标准输出,右边只能是文件
【进程差异】
管道触发两个子进程执行|两边的程序;
重定向是在一个进程内执行
3.转义符:
转义符可以使得元字符被解析为字面含义,共有三种,反斜杠,双引号,以及单引号。三种的转义程度不尽相同。
' '单引号,不具有变量置换(解析)功能($变为纯文本);硬转义,其内部所有的shell 元字符、通配符都会被关掉。
" " 双引号,具有变量置换(解析)功能($可保留相关功能);软转义,其内部只允许出现特定的shell 元字符
\跳脱字符,将特殊字符或者通配符还原成一般字符
3.1 反斜杠 \ ——去除其后紧跟的元字符或通配符的特殊意义。
3.2 双引号 "" ——又称软转义,其内部只允许出现特定的shell 元字符
3.3 单引号 ‘ ’ ——又称硬转义,其内部所有的shell 元字符、通配符都会被关掉。注意,硬转义中不允许出现单引号。
二、正则表达式RE
1.正则表达式的元字符:
这一小节着重讲解正则表达式,因为正则表达式是UNIX工具使用和构建模型上的基础,值得花时间学习并在练习中不断熟悉它。
正则表达式区分BRE以及ERE共两种模式,正则表达式由一般字符和元字符组成。BRE和ERE的元字符稍有不同,如下所示:
【BRE】&【ERE】共享的元字符:
\——用于关闭后续字符的特殊意义。或者用\{...\}
*——用于匹配前面一个普通字符的0次或者多次重复。对于ERE来说,*的前置字符可以是正则表达式,例如.*带表“匹配任一字符的任意长度”
.——用于匹配任意单个字符
^——表示匹配紧接着的正则表达式。(文本匹配锚点)
$——匹配前面的正则表达式。(文本匹配锚点)
[]——匹配方括号内的任一字符。
【BRE】
\{n\}——匹配前面字符出现n次,如[a-z]\{5\}表示精确匹配5个小写英文字母
\{n,\}——匹配前面字符至少出现n次
\{n,m\}——区间表达式。匹配前面字符出现n--m次
\( \)...\n —— \(与\)之间的模式存储在特殊的保留空间,最多可以将9个独立的自模式存储在单个模式中。子模式可通过转义序列\1至\9,被重复使用在相同模式中,\n指的是“匹配于第n个先前方括号内子表达式匹配成功的字符”。如\(ab\).*\1指的是匹配于ab组合的两次重现,中间可存在任何数目的字符。如\(ab\)\(cd\)[def]*\2\1表达的是abcd...cdab,中间可以是def字符集合的任意组合,故可以匹配的最小字符串是abcdcdab,还可以匹配abcdeeecdab,abcdddeeffcdab。这种机制是BRE提供的后向引用机制,指的是“匹配于正则表达式匹配的先前的部分”。在ERE里,\(..\)不表示后向引用,匹配的是字面上的左括号和右括号。
【ERE】—— 正则表达式的扩展
{}——区间表达式,表示匹配前面的单个字符重现的次数区间。不需要反斜杠。
? ——匹配?之前的正则表达式0次或者1次,如JO?B可以匹配JOB,JOOB
+ ——匹配+之前的正则表达式1次或者多次,如S+EU可以匹配SSEU,SSSEU等,但不可以表示SEU因为此处的+至少匹配一个S
|——表示或者,匹配|之前或者之后的正则表达式
【正则表达式的扩展】
\< \>分别匹配单词word的开头和结尾。例如/
【UNIX程序及其正则表达式类型】
grep: BRE,\< \>
sed: BRE,\< \>
ed: BRE,\< \>
ex/vi: BRE,\< \>
more: BRE,\< \>
egrep:ERE
awk:ERE
lex:ERE
记住我们的Linux三剑客,sed, awk,grep,他们所支持的正则是不同的哦。
注意:横杠字符“-”虽然不是正则表达式的元字符,但由于横杆字符是引出命令选项的特殊字符,所以需要用引号和转义符来表示。例如,想要在1.txt文件中找到以“-----”连续五个横杠符号开头的字符串,应该使用以下的命令
grep "\-\{5\}" 1.txt
那么“-----”可不可以呢?答案是不可以。无论你是用"\-\{5\}"还是"\-\-\-\-\-"来表示连续5个“-”,反斜杠转义符都是少不了的。预知原因为何,请看第3小点转义符中相关说明。
三、Shell命令——文本神器三兄弟 awk,grep,sed
grep
grep家族有三兄弟:grep , fgrep , egrep
grep: 标准
egrep:扩展grep命令,支持正则表达式(基本和扩展),等价于grep -E
fgrep:快速grep命令,不支持正则表达式,按照字符串的字面意思进行匹配,等价于grep -F
一般来说,egrep和fgrep极少使用
awk
awk是一种能对结构化数据进行操作,并产生格式化报表的工具
sed
非交互式文本编辑器,可对文本文件和标准输入(键盘输入、文件重定向、字符串、变量or来自管道的文本)进行编辑(输出、插入、删除、替换等)
sed只是对缓冲区中原始文件的副本进行编辑,不编辑原本的文件
sed调用方式:
(1)在Shell命令行输入命令调用sed:
sed 'sed-command' input-file>result-file
(2)将sed命令插入脚本文件之后,通过sed命令调用它
sed -f sed脚本文件 input-file
-f表示正在调用sed脚本文件
(3)将sed命令插入脚本文件之后,设置该脚本文件为可执行,然后直接执行该脚本。(这种方式的sed脚本文件需要以sha-bang(#!)开头)
./sed脚本文件 input-file
sed命令的组成:
一般由定位文本行和sed编辑命令(可以放在单引号内或者单引号外)两部分组成,sed提供两种方式定位文本:
(a)使用行号,指定一行,或者指定行号范围
(b)使用正则表达式
sed编程的栗子
四、其他Shell命令
1. find
2.sort 排序
3.uniq 去重【sort和uniq经常配套使用,毕竟生活中许多统计场景都需要去重、排序、统计频率等操作】
4.head和tail
比较常见的用法是head -n file.txt和tail -n file.txt,用于取file.txt文件的前n行和后n行
次常见且比较灵巧的用法还有 head -n-10 file.txt和tail -n+10 file.txt等
前者代表除了最后10行数据之外,显示其余所有内容。【排除后十行】
后者代表从文本文件第10行开始,取其后部。【从第十行开始】
五、Shell例题
【例1】习题:leetcode 195 《第十行》
给定一个文本文件 file.txt,请只打印这个文件中的第十行。
示例:
假设 file.txt 有如下内容:
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
你的脚本应当显示第十行:Line 10
链接:https://leetcode-cn.com/problems/tenth-line
方法一:借用python (比较trick的一个写法,适合shell救急)
方法二:head tail大法好
对于tail和head的使用,有两种思路:
一种是取前十行的最后一行head -10 file.txt|tail -1,这种思路需要考虑文件的实际行数
若超过十行,则可直接取最后一行,即为第十行( head+tail 夹逼法)
若不超过十行,直接对head -10 file.txt做tail操作实际上取到的是第6、7、8、9行(视file.txt实际行数而定)
另一种是取从第十行开始的内容,取其第一行。这种思路无需对总行数小于10的情况做检验,会自动返回空字符串,符合题目要求。
tail -n+10 file.txt|head -1
方法三:巧用awk sed
摘抄力扣大佬答案
-n选项表示:不打印sed编辑对象的全部内容
【例2】
grep "([0-9]\{3\})" number.txt 不含转义标志"\","("与")"直接匹配中字符串"(234)"中的双括号
grep "\([0-9]\{3\}\)" number.txt 含转义字符,不参与字符匹配
练习题:详见leetcode 193 有效电话号码
六、参考资料
1、Leetcode https://leetcode-cn.com/
2、OSCHINA https://my.oschina.net/badboy2/blog/478953
3、 《Shell脚本学习指南》机械工业出版社