shell编程5-函数与正则表达式

一.case语句

case语句为多选择语句.可以用case语句匹配一个值与一个模式,如果匹配成功,执行相
匹配的命令.

case var in             定义变量;var代表是变量名
pattern 1)              模式1;| 分割多个模式,相当于or
    command1            需要执行的语句
    ;;                  两个分号代表命令结束
pattern 2)
    command2
    ;;
pattern 3)
    command3
    ;;
		  *)              default,不满足以上模式,默认执行*)下面的语句
    command4
    ;;
esac							esac表示case语句结束

案例1

  • 脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭,重启等
[root@shell ~]# vim case.sh            
#!/bin/bash
read -p "请输入要操作的服务名称(nginx):" service
read -p "请输入要对服务执行的操作:" action
case $action in
        start|S)
        systemctl start $service
        echo "$service is running"
        ;;
        stop|T)
        systemctl stop $service
        echo "$service is stoped"
        ;;
        restart|R)
        systemctl restart $service
        echo "$service is restart"
        ;;
        *)
        echo "请输入正确的操作指令"
        ;;
esac
[root@shell ~]# source case.sh 
请输入要操作的服务名称(nginx):vsftpd   
请输入要对服务执行的操作:restart
vsftpd is restart
[root@shell ~]# source case.sh 
请输入要操作的服务名称(nginx):nginx
请输入要对服务执行的操作:status
请输入正确的操作指令

案例2

  • 模拟一个多任务维护界面.当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作.
[root@shell ~]# vim case2.sh 
#!/bin/bash
cat <<-EOF
        h       显示命令帮助
        f       显示磁盘分区
        d       显示磁盘挂载
        m       查看内存使用
        u       查看系统负载
        q       退出程序
EOF
# <<-EOF将忽略起止内容中前面的tab制表符,而<<EOF将不会
while true
do
        read -p "请输入需要的选项:" var1
        case $var1 in
                h)
                cat <<-EOF
                        h       显示命令帮助
                        f       显示磁盘分区
                        d       显示磁盘挂载
                        m       查看内存使用
                        u       查看系统负载
                        q       退出程序
                EOF
                ;;
                f)
                fdisk -l
                ;;
                d)
                df -h
                ;;
                m)
                free -m
                ;;
                u)
                untime
                ;;
                q)
                echo "已退出程序"
                break
                ;;
esac
done
[root@shell ~]# source case2.sh 
h       显示命令帮助
f       显示磁盘分区
d       显示磁盘挂载
m       查看内存使用
u       查看系统负载
q       退出程序
请输入需要的选项:u
 16:33:40 up 2 days, 18 min,  1 user,  load average: 0.01, 0.02, 0.00
请输入需要的选项:^C
[root@shell ~]# source case2.sh 
h       显示命令帮助
f       显示磁盘分区
d       显示磁盘挂载
m       查看内存使用
u       查看系统负载
q       退出程序
请输入需要的选项:u
 16:33:44 up 2 days, 18 min,  1 user,  load average: 0.01, 0.02, 0.00
请输入需要的选项:q
已退出程序

二.函数

shell中允许将一组命令集合或语句形成一段可用代码,这些代码块称为shell函数.给这段代码起个名字称为函数名,后续可以直接调用该段代码的功能.

1.函数定义

函数名()
{
  函数体(一堆命令的集合,来实现某个功能)   
}

function 函数名()
{
   函数体(一堆命令的集合,来实现某个功能)  
}


function_name() {
		command
		command
}


function function_name() {
		command
		command
}


函数中return说明:
1.return可以结束一个函数,类似于前面讲的循环控制语句break(结束当前循环,执行循环体后面的代码)
2.return默认返回函数中最后一个命令的退出状态,也可以给定参数值,该参数值的范围是0-256之间.
3.如果没有return命令,函数将返回最后一个Shell的退出值.

2.函数的调用

  • 当前命令行调用
[root@shell-note ~]# vim fun1.sh
hello(){
#!/bin/bash
hello(){
        echo "hello xiaomei $1"
}       
menu(){
        cat <<-EOF
        1. mysql
        2. web
        3. app
        4. exit
        EOF
}
[root@shell-note ~]# source fun1.sh 
# 生效函数
[root@shell-note ~]# hello 111
# 调用函数
hello xiaomei 111
[root@shell-note ~]# menu 
1. mysql
2. web
3. app
4. exit
  • 定义到用户的环境变量中
[root@shell-note ~]# vim .bashrc 
# .bashrc
  
# User specific aliases and functions

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

# 添加如下内容
hello(){
        echo "hello xiaomei $1"
}
menu(){
        cat <<-EOF
        1. mysql
        2. web
        3. app
        4. exit
        EOF
}
[root@shell-note ~]# source .bashrc
# 生效函数
[root@shell-note ~]# hello 111
hello xiaomei 111
[root@shell-note ~]# menu 
1. mysql
2. web
3. app
4. exit
  • 脚本中调用
[root@shell-note ~]# vim fun2.sh
#!/bin/bash
menu(){
        cat <<-EOF
        h       hello
        q       quit
        t       test
        EOF
}       
menu
# 调用函数
[root@shell-note ~]# source fun2.sh 
h       hello
q       quit
t       test

3.应用案例

通过跳板机连接生产机器
结合expect脚本

[root@shell-note ~]# dnf -y install expect
[root@shell-note ~]# vim jump-server.sh 
#!/bin/bash
while true
do
menu(){
        cat <<-EOF
        欢迎使用Jumper-server,请选择你要操作的主机:
        1. DB1-Master
        2. DB2-Slave
        3. Web1
        4. Web2
        h. help
        q. exit
        EOF
}
menu
read -p "请选择你要操作的主机:" host
case $host in
        1)
                ./1.sh
                ;;
        2)
                ./2.sh
                ;;
        3)
                ./3.sh
                ;;
        4)
                ./4.sh
                ;;
        h)
                menu
                ;;
        q)
                break
                ;;
esac
done
[root@shell-note ~]# vim 1.sh 
#!/usr/bin/expect
  
spawn ssh root@172.20.251.124
# 启动新的进程
# 其余脚本皆为修改此处ip即可
expect {
        "yes/no" { send "yes\r"; exp_continue }
        "password:" { send "000000\r" };
}
# expect用于从进程接受字符串
# send用于向进程发送字符串
interact
# 允许用户交互

[root@shell-note ~]# chmod +x {1..4}.sh
# 增加expect脚本执行权限
[root@shell-note ~]# sed -i "/expect {/i\set timeout 30" {1..4}.sh 
# 添加超时处理
[root@shell-note ~]# cat 1.sh 
#!/usr/bin/expect
  
spawn ssh root@172.20.251.124
# 启动新的进程
# 其余脚本皆为修改此处ip即可
set timeout 30
expect {
        "yes/no" { send "yes\r"; exp_continue }
        "password:" { send "000000\r" };
}
# expect用于从进程接受字符串
# send用于向进程发送字符串
interact
# 允许用户交互
[root@shell-note ~]# source jump-server.sh 
# 测试脚本
欢迎使用Jumper-server,请选择你要操作的主机:
1. DB1-Master
2. DB2-Slave
3. Web1
4. Web2
h. help
q. exit
请选择你要操作的主机:4
spawn ssh root@172.20.251.164
root@172.20.251.164's password: 
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Fri Sep 16 08:51:27 2022 from 172.20.251.192
[root@shell ~]# 
# 成功登录生产服务器

实战不推荐,仅当练手项目,因为密码暴露在脚本中不是安全的

四.正则表达式

1.什么是正则表达式

正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。

许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。

正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。

支持正则表达式的程序如:locate |find| vim| grep| sed |awk

2.第一类正则

  • 名词解释:

元字符:指那些在正则表达式中具有特殊意义的专用字符,如:点(.) 星(*) 问号(?)等

前导字符:即位于元字符前面的字符 abc* aooo.

  • 正则中常用的元字符
(1).		任意单个字符,除了换行符 
(2)*		前导字符出现0次或连续多次  ab*能匹配“a”,“ab”以及“abb”,但是不匹配“cb”
(3).*		任意长度的字符  ab.*  ab123 abbb abab
(4)^		行的开头
(5)$		行的结尾
(6)^$		空行


(7)[]		匹配指定字符组内的任一单个字符   [abc]
(8)[^]	匹配不在指定字符组内的任一字符 	[^abc]

(9)^[]	匹配以指定字符组内的任一字符开头   ^[abc]
(10)^[^]	匹配不以指定字符组内的任一字符开头  ^[^abc] 



(11)\<		取单词的头
(12)\>		取单词的尾
(13)\<\>		精确匹配符号 	grep -w 'xxx'


(14)\{n\}	匹配前导字符连续出现n次    go\{2\}   google  gooogle
(15)\{n,\}	匹配前导字符至少出现n次 
(16)\{n,m\}	匹配前导字符出现n次与m次之间 


 (17) \(strings\)	保存被匹配的字符
[root@shell-note ~]# vim 1.txt
172.20.251.1
:%s/\(172.20.251\).1/\1.254/g  
# 替换172.20.251.1为172.20.251.254
172.20.251.254

# sed -n 's#\(192\.168\)\.0\.254#\1\.1\.254#p'      
找出含有192.168的行,同时保留192.168并标记为标签1,之后可以使用\1来引用它。最多可以定义9个标签,从左边开始编号,最左边的是第一个。
\1 就代表被匹配到的第一个模式,sed 一共可以记录9个模式。
[root@shell-note ~]# sed -n "s/\(192.168.10\).1/\1.254/p" 1.txt           
192.168.10.254
[root@shell-note ~]# sed -n "s/\(192.168\).10.1/\1.254/p" 1.txt 
192.168.254

将you are the hero 替换为we are family
[root@shell-note ~]# vim 1.txt 
10.1.1.254
192.168.10.1
you are hero
:%s/you \(are\) the hero/we \1 \2family/p
we are family
# 同理,使用sed可替换为如下命令
[root@shell-note ~]# sed -n "s/you \(are\) the hero/we \1 family/p" 1.txt    
we are family
[root@shell-note ~]# sed -n "s/you are the hero/we are family/p" 1.txt  
we are family
# -n为只显示匹配行,p将结果打印出来
# 如果要改变文件内容使用i参数替换
[root@shell-note ~]# sed -i "s/you \(are\) the hero/we \1 family/g" 1.txt  
[root@shell-note ~]# cat 1.txt 
10.1.1.254
192.168.10.1
we are family
# 此处不使用-r参数,因为-r代表使用扩展正则表达式

[0-9] [a-z] [A-Z] [a-zA-Z] [a-Z]
=================================================================================
Perl内置正则:
\d      匹配数字  [0-9]
\w      匹配字母数字下划线[a-zA-Z0-9_]
\s      匹配空格、制表符、换页符[\t\r\n]

#grep -P '\d' test.txt
#grep -P '\w' test.txt
#grep -P '\s' test.txt
[root@shell-note ~]# grep -P "\d" 1.txt 
10.1.1.254
192.168.10.1
[root@shell-note ~]# grep -P "\w" 1.txt  
10.1.1.254
192.168.10.1
you are the hero
[root@shell-note ~]# grep -P "\s" 1.txt  
you are the hero
  • 扩展类的正则表达式 grep -E 或则 egrep
扩展正则表达式元字符
+			匹配一个或多个前导字符		bo+	boo bo
?			匹配零个或一个前导字符		bo?	b bo

a|b		匹配a或b
()			组字符	   hello myself yourself     (my|your)self

{n}		前导字符重复n次			\{n\}
{n,}		前导字符重复至少n次	  \{n,\}
{n,m}		前导字符重复n到m次		\{n,m\}

[root@shell-note ~]# grep '[0-9]\{2\}\.[0-9]\{1\}\.[0-9]\{1\}\.[0-9]\{1\}' 1.txt 
10.1.1.1
[root@shell-note ~]# grep -E '[0-9]{2}\.[0-9]{1}\.[0-9]{1}\.[0-9]{1}' 1.txt
10.1.1.1
[root@shell-note ~]# grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' 1.txt 
10.1.1.1
192.168.10.1
[root@shell-note ~]# grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' 1.txt 
10.1.1.1
192.168.10.1

3.第二类正则

表达式功能示例
[:alnum:]字母与数字字符[[:alnum:]]+
[:alpha:]字母字符(包括大小写字母)[[:alpha:]]{4}
[:blank:]空格与制表符[[:blank:]]*
[:digit:]数字[[:digit:]]?
[:lower:]小写字母[[:lower:]]{4,}
[:upper:]大写字母[[:upper:]]+
[:punct:]标点符号[[:punct:]]
[:space:]包括换行符,回车等在内的所有空白[[:space:]]+
[root@shell-note ~]# grep -E '^[[:digit:]]+' 1.txt
10.1.1.1
192.168.10.1
[root@shell-note ~]# grep -E '^[^[:digit:]]+' 1.txt
you are the hero
[root@shell-note ~]# grep -E '[[:lower:]]{4,}' 1.txt
you are the hero

4.实践操作

[root@shell-note ~]# vim test.txt 
1111111111
2222222222


2022-09-20
helloworld
welcome to test.

192.168.10.254
10.1.1.20
AI2test
S1mple
tbase
123@qq.com
abc
123456

实现如下要求:

1.查找不以大写字母开头的行
2.查找有数字的行(两种写法)
3.查找一个字母和一个数字连起来的
4.查找不以t开头的行
5.查找以数字开头的
6.查找以大写字母开头的
7.查找以小写字母开头的 
8.查找以点结束的
9.去掉空行
10.去掉带空格的空行
11.查找完全匹配123456的行
12.查找ip并将最后的数字都替换为254
13.查找hello开头的行并将之后内容替换为program
14.统计/sbin/nologin在/etc/passwd里出现了几次
15.找出全部是数字的行
16.使用正则表达式给仅有数字行前加两0
17.查找邮箱账号

答案例(不止一种方法,结果正确即可)

1.
[root@shell-note ~]# grep "^[^A-Z]" test.txt 
2.
[root@shell-note ~]# grep "[0-9]" test.txt 
[root@shell-note ~]# grep -E "[[:digit:]]+" test.txt
3.
[root@shell-note ~]# grep "[A-Za-z][0-9]" test.txt 
4.
[root@shell-note ~]# grep "^[^t]" test.txt
5.
[root@shell-note ~]# grep "^[^0-9]" test.txt
6.
[root@shell-note ~]# grep "^[A-Z]" test.txt 
7.
[root@shell-note ~]# grep "^[a-z]" test.txt 
8.
[root@shell-note ~]# grep "\.$" test.txt 
# 记得给点加上转义字符
9.
[root@shell-note ~]# sed -i "/^$/d" test.txt 
10.
[root@shell-note ~]# sed -i "/^\s*$/d" test.txt
11.
[root@shell-note ~]# grep "123456" test.txt 
123456
12.
[root@shell-note ~]# sed -n "s/\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).[0-9]\{1,3\}/\1.254/p" test.txt 
# 此处只为显示ip替换后的结果
# 下面的命令才是替换文本中的ip内容
[root@shell-note ~]# sed -i "s/\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).[0-9]\{1,3\}/\1.254/g" test.txt
13.
[root@shell-note ~]# sed -n "s/\(^hello\).*/\1program/p" test.txt 
[root@shell-note ~]# sed -i "s/\(^hello\).*/\1program/g" test.txt   
14.
[root@shell-note ~]# grep -c "/sbin/nologin" /etc/passwd
15.
[root@shell-note ~]# grep -x "[0-9]*" test.txt 
# -x 参数会查找字符串 exact 的匹配,即全字匹配
16.
[root@shell-note ~]# sed -n "s/\(^[0-9][0-9]*[0-9]$\)/00\1/p" test.txt 
001111111111
002222222222
00123456
17.
[root@shell-note ~]# grep "[0-9]*@[a-z0-9]*\.[a-z]*" test.txt 
[root@shell-note ~]# grep -E '^[0-9]+@[a-z0-9]+\.[a-z]+$' test.txt

# grep参数详解
grep --help:
匹配模式选择:
Regexp selection and interpretation:
  -E, --extended-regexp     扩展正则
  -G, --basic-regexp        基本正则
  -P, --perl-regexp         调用perl的正则
  -e, --regexp=PATTERN      use PATTERN for matching
  -f, --file=FILE           obtain PATTERN from FILE
  -i, --ignore-case         忽略大小写
  -w, --word-regexp         匹配整个单词

5.正则总结

元字符:在正则中,具有特殊意义的专用字符 . *
前导字符:元字符前面的字符叫前导字符

元字符字符说明示例
*前导字符出现0次或者连续多次ab* abbbb
.除了换行符以外,任意单个字符ab. ab8 abu
.*任意长度的字符ab.* adfdfdf
[]括号里的任意单个字符或一组单个字符[abc][0-9][a-z]
[^]不匹配括号里的任意单个字符或一组单个字符[^abc]
^[]匹配以括号里的任意单个字符开头^[abc]
^[^]不匹配以括号里的任意单个字符开头^[^abc]
^行的开头^root
$行的结尾bash$
^$空行
\{n\}和{n}前导字符连续出现n次[0-9]\{3\}
\{n,\}和{n,}前导字符至少出现n次[a-z]{4,}
\{n,m\}和{n,m}前导字符连续出现n-m次go{2,4}
\<\>精确匹配单词\<hello\>
\(\)保留匹配到的字符\(hello\)
+前导字符出现1次或者多次[0-9]+
?前导字符出现0次或者1次go?
|^root|^ftp
()组字符(hello|world)123
\dperl内置正则grep -P \d+
\w匹配字母数字下划线
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值