shell脚本介绍
shell是脚本编程语言,是一门解释型语言,这类程序的执行,是有解释器读入程序代码,并将其转换为内部的形式,在执行。解释器本身就是编译型程序。
我们使用shell编程的主要是因为Linux系统管理需求,通过shell脚本帮助我们自动完成某些工作任务,提升工作效率。
shell程序设计原则
- 一次做好一件事
进行shell程序编写,要一次就只干好一件事即可,还有其他的功能需要实现,那就把程序以空行进行分开,加上注释,这样可以让程序更清晰,更直观,无论是进行程序设计,编写,调试,测试,维护都是非常方便的。
- 使用正则表达式
在进行shell脚本编写的时候,发现很多时候,都需要使用正则进行结果匹配,不使用正则的话,会导致结果匹配难度加大,嗯,写好正则准没错。
- 使用默认的标准输入及输出
在未明确指定文件名的情况下,程序默认会从它的标准输入读取数据,将数据写到它的标准输出,程序错误默认会传送给标准错误输出,使用程序默认的规则既方便自己维护也方便其他同事进行维护,按照标准来,方便你我他。
- 构建程序结构
写程序之前,应该对程序实现功能进行具体定位,了解当前程序是为了实现什么功能,怎么实现,实现流程等,花个时间,建个大体的程序构建流程。
shell编程
- shell调用
由于现在的Linux系统中有很多shell,那么在shell程序中,shell如何知道我们调用哪个shell,那就在第一行 #!+指定shell路劲即可,在Linux系统中选择默认的/bin/bash即可(选择shell需要满足POSIX标准的才行)
例如:
#!/bin/bahs
shell基本元素
- 命令和参数
shell程序最基础的是Linux命令,你对命令很了解,就可以通过shell轻松玩转Linux。
shell识别哪些命令:Linux中只要执行的命令都支持,无论是Linux内建的,shell函数还是外部命令都支持,并且命令执行方式跟你在Linux中执行命令的方式是一样,需要注意的是你在Linux执行命令需要加绝对路劲的话,那在shell脚本一定要加,不然就识别不了,建议不管啥命令都加绝对路劲,还有是只能运行你当前用户所能运行的命令。
# vim wc.sh
#!/bin/bash
who | wc -l
# sh wc.sh
2
- 变量
变量是shell 传递数据的一种方法。变量是用来代表每个值的符号名。我们可以把变量当成一个容器,通过变量,可以在内存中存储数据。也可以在脚本执行中进行修改和访问存储的数据
- 变量的设置规则:
1、 变量名称通常是大写字母,它可以由数字、字母(大小写)和下划线_组成。变量名区分大小写;但是大家要注意变量名称不能以数字开头
2、 等号= 用于为变量分配值,在使用过程中等号两边不能有空格
3、 变量存储的数据类型是整数值和字符串值
4、 在对变量赋于字符串值时,建议大家用引号将其括起来。因为如果字符串中存在空格隔符号。需要使用单引号或双引号
5、 要对变量进行调用,可以在变量名称前加美元符号$
6、 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含“$变量名”或用${变量名}包含
- 变量的分类
按照变量的作用可以分成 4类:
1、用户自定义变量
2、环境变量:这种中主要保存的是和系统操作相关数据。
3位置参数变量:这种主要是用来向脚本当中传递或据的,名不能自定 义作固的。
4、预定义变量:是 BashBashBash 中已经定义好的变量,名不能自作用也是固。 中已经定义好的变量,名不能自作用也是固。
按照变量作用域可以分成 2类:全局变量和部。
局部变量是 shell shell shell shell 程序内部定义 的,其使用范围仅限于它对不可见。包括:户自程序内部定义 的,其使用范围仅限于它对不可见。包括:户自变量、位置和预定义。
全局变量是环境变量,其值不随 全局变量是环>境,其值不随 shell shell shell shell 脚本的执行结束而消失。
正则表达式
正则表达式是一种匹配模式,通过正则表达式,用户可以精准匹配自己想要的内容。
正则表达式基本是由2个部分组成,分别是一般字符及特殊字符。一般字符是指没有任何意义的字符,也就是说字符没有特殊含义,比如,a,b,c,d等字符;特殊字符是指拥有特殊含义,并且在一定情况下可以转为一般字符那种,也被称为元字符。
正则表达式可以分为基础正则表达式(BRE)及扩展正则表达式(ERE)。
- 基础正则表达式
- 常见的特殊字符:
BRE/ERE | 特别字符 |
描述(默认以BRE功能为主) |
2 | $ |
匹配输入字符串的结尾位置。要匹配$ 字符本身,ERE可以放在任意位置 |
BRE | ( ) |
标记一个子表达式的开始和结束位置。要匹配这些字符,请使用\( 和\) |
2 | * |
匹配前面的子表达式零次或多次。要匹配* 字符,请使用\*(位于第一个字符就没有任何意义,) |
2 | + |
匹配前面的子表达式一次或多次。要匹配+ 字符,请使用\+ |
2 | . |
匹配除换行符\n 之外的任何单字符。要匹配. ,请使用\. ;nul除外 |
[ |
标记一个中括号表达式的开始。要匹配[,请使用\[ |
|
2 | ? |
匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配? 字符,请使用\? |
\ |
将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如,'n' 匹配字符'n'。'\n' 匹配换行符。序列'\\' 匹配"\",而'\(' 则匹配"(" |
|
2 | ^ |
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配^ 字符本身,请使用\^;ERE置于任何位置都有意义 |
{ |
标记限定符表达式的开始。要匹配{,请使用\{ |
|
2 | [...] | 方括号表达式,匹配方括号内的任一字符, [a-c],连字符,指的是范围,[^a],^放在方括号里面,则是取反的意思,不匹配里面任一字符 |
BRE | \{n,m\} | 区间表达式,匹配它前面单个字符重现的次数。\{n\}指重现n次, \{n,\}至少重现n次,\{n,m\}重现n到m次 ,n与m的值介于0到RE_DUP_MAX之间,centos7中,最大值是32767 # getconf RE_DUP_MAX |
BRE | \( \) | \( "保留空间"\) 主要是存储子表达式,最多可以存储9个 |
ERE | {n,m} | 是属于ERE独有的区间表达式 |
2 | | |
指明两项之间的一个选择。要匹配|,请使用\| 如:Y | y |
定位符 |
||
^ |
匹配输入字符串开始的位置 |
|
$ |
匹配输入字符串结尾的位置 |
|
非打印字符 |
||
BRE | \s |
空格 \s*0个或者多个空格 |
\n |
匹配一个换行符 |
|
\r |
匹配一个回车符 |
|
\t |
匹配一个制表符 |
- 常见表达式:
# cat wc.sh
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "touch" #只打印"touch"几个字符,跟常规grep匹配差不多
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "^ma" #匹配以"ma"字符开头的行
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "mysql$" #匹配以"mysql"字符结尾的字符,跟命令行中的$差不多
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "^matchtouchmkdirnginxmysql$" #匹配完整的内容,也就是这一行只能且只有这些内容
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "[nN]ginx" #[ ]里面是匹配单个字符,只要满足这个要求,无论是啥字符都行,同时在很多命令中,[ ]里面的内容是可选的
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.m" #匹配c-m之间的单个字符
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.*m" #匹配c-m之间的0个或者多个字符
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.+m" # + 是匹配前面子表达式的1个或者多个字符
matchtouchmkdirnginxmysql
# cat wc.sh | egrep "c.?m" # ? 匹配前面0个或者1个字符
matchtouchmkdirnginxmysql
- 常见特殊字符集(在shell里面实践了一下好像跑不了,据说是php的正则表达一种):
-
基础正则表达式
支持单个字符匹配及多个字符匹配 (用到的字符就是BRE里面的所有标准字符及特殊字符)
[ : : ] 字符集,可以把多个字符组合起来成为一个完整的字符串
[ . . ] 排序元素 主要是进行排序
[ = ] 等价字符集,也就2边的字符都是相等的
这几种字符集方式的实现都需要[ ] 来帮忙实现。
在 [ ] 中所有字符都会失去其特殊含义。[ * \ . ] 就是字面上的意思,正确写法:[ ] * \ .]
-
分组
分组简单来说是把一串字符串,通过 \( \) 方式形成一个子表达式(单个字符),供后面的正则去匹配,并且最多存储9个子表达式,用数字1-9表示。1个 \( \) 就是一个分组,就是一个整体。
$ cat ac.txt
hello
helloo
hellooo
hellohel
hellohell
hellohello
$ cat ac.txt | grep "hello\{3\}" #\{n\}在BRE里面是匹配前面单个字符出现的次数
hellooo
$ cat ac.txt | grep "\(hello\)\{2\}"
hellohello
#在上面的表里面,有个 \( \) 特殊字符,这里面主要是存放子表达式的,也就是把内容整合为一个整体,
供后面的正则去匹配,简单来说就是把多个字符组合一起,成为单个字符
分组还可以嵌套的:
$ cat ac.txt | tail -3
hellohello
queuequeue
queue
$ cat ac.txt | grep '\(q\(ue\)\{2\}\)\{2\}'
queuequeue
#这就是个简单的嵌套,也就是这样:\( \( \) \)优先运行里面的\( \) 然后在运行外面最大的\( \)
上面例子解析:
首先运行 q\(ue\)\{2\} 结果是queue,也就是把ue作为一个整体匹配了出现的次数也就是2次
然后运行最外面的 \(queue\)\{2\},结果就是queuequeue,在进行子表达式嵌套的时候会把最里面子
表达的结果给外面的子表达式作为结果输入,跟Linux中的|一个意思。
grep '\(ue\)\{2\}\)' 与 grep '\(ue\{2\}\)' 是有区别的,前者是把ue作为一个字符,后者是把e作为字符
$ cat ac.txt | grep '\(ue\)\{2\}'
queue
$ cat ac.txt | grep '\(ue\{2\}\)'
quee
-
后向引用
$ cat 7.txt
tzc word yunying
lij word lij
haibing word yunying
需求1:打印tzc那一行
$ cat 7.txt | grep 't.\{2\}\s*word y.\{3,8\}' # . 是表示除\n之外的任意单个字符,这个表示只匹配tzc了
tzc word yunying
需求2:匹配word字段周边都相同的行
$ cat 7.txt | grep '\(l.\{2\}\)\s*word \1' #出现全新玩法 \1
lij word lij
注:\1是什么了?这个就是后向引用,也就是\1是引用第一个分组的结果,\( \)这就是分组括号,里面的结果就是分组结果,\1就是匹配第一个分组的结果。
严重强调:\1 \2等等只能匹配\( \)的结果
$ cat test.txt
abccccabeeee word abccccabeeee
$ cat test.txt | grep '\(\(abcc.\{2\}\)\ab.\{4\}\) word \2'
abccccabeeee word abccccabeeee
$ cat test.txt | grep '\(\(abcc.\{2\}\)\ab.\{4\}\) word \1'
abccccabeeee word abccccabeeee
根据上面结果显示,后向引用顺序是由左侧分组符号进行排序的也就是 ((( 分别是\1 \2 \3
后向引用需要以分组为前提的不然就引用不了,后向引用只能引用分组符号 \( \)的匹配结果
\1 表示引用第一个分组括号里面的正则匹配结果,\2 \3依次类推
-
优先级
BRE运算符优先级,由高至低
运算符 | 表示含义 |
[..] [==] [::] | 用于字符排序的方括号符号 |
\metacharacter | 转义的 meta 字符 |
[ ] | 方括号表达式 |
\( \) \digit | 子表达式与后向引用 |
*\{ \} | 前置单个字符重现的正则表达式 |
无符号(no symbol) | 连续 |
^ $ | 锚点 |
- 扩展正则表达式
ERE是扩展正则表达式,扩展正则表达式在标准正则表达式上新增一些功能,能满足大家的工作需求。
单个字符匹配跟跟BRE方法是差不多的,但是ERE中的awk里面 \ 有特殊的含义。
- 反向应用:在通过sed进行扩展正则的时候一样可以使用
- 字符匹配:ERE在字符匹配方面,与BRE有明显的不同,其中*的功能是一样的。
- 区间表达式:ERE也存在区间表达式,但是不在是 \{ \},而是以 { } 这种形式展现,且不需要前置 \ 字符。
- 分组:ERE进行分组的话是 ( ),不需要在加 \ 字符
- 交替: | 跟BRE方式一样,二选一那种 (read|write) 还有是添加锚点符那种 ^action|active$ 匹配字符串开始处是否有action或者结尾处是否有active ^(ac|bc)$匹配当前内容里面是否有ac或者bc字符串
-
优先级:跟BRE是一样的由高到低
-
额外正则运算表达符
运算符 | 表示含义 |
\w | 匹配任意单词(可以是单个字符,字符串等等) ac bh ki js n r |
\W | 匹配任意符号(可以是单个,也可以是多个) ? \ + - == [ ] { } -- () |
\< \> | \<ac 匹配以ac开头的字符 df\> 匹配以df结尾的字符 \<qwe\> 那就只匹配qwe字符 |
\b | 单词边界,就是单词和符号之间的边界,单词就是\w匹配的内容,符号是\W匹配的内容,也就是匹配字跟空格之间的一个位置 # cat wc.sh # cat wc.sh | egrep 's\b' # cat wc.sh | egrep '\bma' # cat wc.sh | egrep '\bac\b' 总结:通过\b可以匹配开头处的内容及结尾处的内容,至于中间的内容需要\B来匹配 注:更深层次的理解,等我理解完毕再来补充 |
\B | 非单词边界,是单词与单词,符号与符号之间的边界 # cat wc.sh | egrep '\B1223\B' 总结:简单来说就是匹配某个内容是否存在 注:更深层次的理解,等我理解完毕再来补充 |
非常好的正则使用例子:
root@test:/opt/scripts 12:47:52
$ cat hl-ip.txt.bak | sort | awk -F'/' '{print $3,$0}' | sed -r 's/.*\.(\w+).(com|net) /\1.\2 /'| sort -k 1 | awk '{print $2}'
http://10.pic.123456.com/2017-08/mB8CBuXo8u87MFthMmWSEJymrQHS2G5hkgWSAR2q.jpeg
http://1.pic.234567.com/2017-07/FLeGYVYY5EPakINvcORvJvxoR9z19HG2McSuAeww.jpeg
位置参数
位置参数分别是$0 $1 $2.....$n
# sh system.sh 1 2 3 4
根据上面那个命令展示:$0 就是命令本身 1 是$1 2 是$2 3是$3 依次类推
程序跟踪测试
在Linux命令行:sh -x 叫脚本 #一般都是使用这种方式
在shell脚本里面:以set -x开启跟踪模式 以set -x关闭跟踪模式,例如:
#!/bin/bash
who | wc -l >/dev/null
set -x
if [ $? = 0 ]
then
echo "zhixingchenggong"
else
echo "zhixingfailed"
fi
set -x
echo abc
root@test:/opt/scripts/my.scripts 18:52:22
# sh wc.sh
+ '[' 0 = 0 ']'
+ echo zhixingchenggong
zhixingchenggong
+ set -x
+ echo abc
abc
特殊文件
/dev/null 和 /dev/tty
/dev/null 熟称黑洞,将标准输出输出到这个文件后,输出结果会被系统给丢掉,不占用系统任何空间,非常的实用。
/dev/tty 当程序打开此文件时,shell会自动将它重定向到一个实体终端(不怎么用过)
文本输出
echo
printf
printf是模仿C程序库的printf()库里程序,但是功能相当于与echo而言,更复杂,更灵活。printf不支持自动换行,echo支持自动换行
root@test:/opt/scripts/my.scripts 17:45:12
# printf "hello word"
hello wordroot@test:/opt/scripts/my.scripts 17:45:37
# echo "hello word"
hello word
root@test:/opt/scripts/my.scripts 17:47:38
# printf "hello word\n"
hello word
注:根据上图所示,printf不支持换行,需要加\n才行
用法:printf FORMAT [ARGUMENT]...
转载:https://blog.csdn.net/yts1115084429/article/details/101998237