shell中模式匹配+函数+正则

2026-05-13 23:32:3237 阅读量

一、case语句

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

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

当给程序传入start、stop、reload三个不同参数时分别执行相应命令。

#!/bin/bash
case $1 in
	start|S)
	read -p "Input your service name:" name
	case $name in
		apache)
		service apache start
		echo "$name service is running...."
		;;
		mysql)
		service mysql25 start
		echo "$name service is running..."
		;;
	esac
	;;
	stop|P)
	echo "service is stoped...."
	;;
	reload|restart)
	echo "service is restart..."
	;;
	*)
	echo "the $0 usage:$0 start|stop|reload..."
	;;
esac


#!/bin/bash
#up service
case $1 in
	start)
	read -p "Input your start service[vsftpd]:" var1
	case $var1 in
		vsftpd)
		service vsftpd start
		;;
		apache)
		service apache start
		;;
		'')
		true
		;;
		*)
		echo "请输入你要启动的服务,谢谢"
		;;
	esac
	;;
	stop|S)
	service iptables stop
	;;
	reload|restart)
	service iptables restart
	;;
	*)
	echo "Input your action..."
	;;
esac


案例2

模拟一个多任务维护界面。当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作。

	h	显示命令帮助
	f	显示磁盘分区
	d	显示磁盘挂载
	m	查看内存使用
	u	查看系统负载
	q	退出程序

分析:
1. 打印菜单	echo cat
2. 等待用户输入需要的操作编号 h f d...	case ... esac


#!/bin/bash
#打印菜单
cat <<-EOF
	h	显示命令帮助
	f	显示磁盘分区
	d	显示磁盘挂载
	m	查看内存使用
	u	查看系统负载
	q	退出程序
	EOF

#让用户输入需要的操作
while true
do
read -p "请输入需要操作的选项[f|d]:" var1
case $var1 in
	h)
	cat <<-EOF
        h       显示命令帮助
        f       显示磁盘分区
        d       显示磁盘挂载
        m       查看内存使用
        u       查看系统负载
        q       退出程序
	EOF
	;;
	f)
	fdisk -l
	;;
	d)
	df -h
	;;
	m)
	free -m
	;;
	u)
	uptime
	;;
	q)
	exit
	;;
esac
done



#!/bin/bash
#打印菜单
menu(){
cat <<-END
	h	显示命令帮助
	f	显示磁盘分区
	d	显示磁盘挂载
	m	查看内存使用
	u	查看系统负载
	q	退出程序
	END
}
menu
while true
do
read -p "请输入你的操作[h for help]:" var1
case $var1 in
	h)
	menu
	;;
	f)
	read -p "请输入你要查看的设备名字[/dev/sdb]:" var2
	case $var2 in
		/dev/sda)
		fdisk -l /dev/sda
		;;
		/dev/sdb)
		fdisk -l /dev/sdb
		;;
	esac
	;;
	d)
	lsblk
	;;
	m)
	free -m
	;;
	u)
	uptime
	;;
	q)
	exit
	;;
esac
done

####练习

  1. 输入一个等级(A-E),查看每个等级的成绩;如:输入A,则显示“90分~100分”,依次类推
  2. 模拟2人第一次相亲的场景,使用read让用户输入它的名字,性别,年龄(年龄放在性别判断后);在case里面再嵌套case菜单,使之选项更丰富。

要求:
1)

  • 对性别进行判断,如果不输入男或者女,则显示”你是泰国来的吗?“
  • 如果是男的,对其年龄进行判断。

2)

  • 如果男的年龄大于等于18岁则显示“某某先生,你结婚了吗?”;
  • 如果对方回答结了或者yes,则显示“结了你来这凑什么热闹”;
  • 如果对方回答没有或者no,再次询问“那你有房有车吗?”;
  • 如果既不说结了也不说没结则显示:“你到底结没结婚啊?”
  • 如果回答有房有车,则显示”咱去民政局领证吧“;
  • 如果回答没有,则显示“不好意思,我去下洗手间。”;
  • 如果既不说有又不说没有,则显示“别浪费时间,请正面回答”。
  • 如果男的年龄小于18岁,则显示“某某某你个小毛孩也来这凑热闹啦”

3)如果是女的,并且年龄大于等于18岁,则显示”某某女士你好“;否则显示”某某小姐你好“

参考:
#!/bin/bash
read -p "输入你的姓名:" name
read -p "输入你的性别:" gender

case "$gender" in
	男|man|male|boy )
		read -p "输入你的年龄:" age
		if [ $age -ge 18 ];then
			read -p "$name先生,你结婚了吗?" anwser
				case "$anwser" in
					结了||yes )
						echo "结了你还来干嘛?"
						;;
					没结|没有||no )
						read -p "有房有车吗?" anwser2
						case "$anwser2" in
							有)
								echo "咱就直接去民政局领证吧"
								;;
							没有 )
								echo "不好意思,我去下洗手间"
								;;
							* )
								echo "别浪费时间,请正面回答"
						esac
						;;
					* )
						echo "你到底结没结?"
				esac

		else
			echo "$name小子"
		fi
		;;|woman|female|girl|lady )
		read -p "输入你的年龄:" age
		if [ $age -ge 18 ];then
			echo "$name女士"
		else
			echo "$name小姐"
		fi
		;;
	* )
		echo "你是泰国来的吗?"
esac

该程序有个bug:如果输入年龄为负数或者0也是可以的,如何修复bug?增加一个条件:如果输入的年龄小于等于10则显示:”不跟你玩了。。。“

在最后加入如下语句即可:
if [ $age -ge 18 ];then
                        echo "$name女士"
                elif
                   [ $age -le 10 ];then
                        echo "不跟你玩了"
                     exit 1
                else
                        echo "$name小姐"
                fi
                ;;
        * )
                echo "你是泰国来的吗?"

esac

二、函数

变量名=变量值

shell中模式匹配+函数+正则

函数——>命令集合 实现某个功能 eg:批量创建5个用户

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

语法结构:
function_name() {
		command
		command
}


function function_name() {
		command
		command
}

函数名()
{
 ......
}

function 函数名()
{
 .......
 command1
 command2
}

函数中return说明:
1.return可以结束一个函数,类似于前面讲的循环控制语句break(结束当前循环,执行循环体后面的代码)
2.return默认返回函数中最后一个命令的退出状态,也可以给定参数值,该参数值的范围是0-256之间。
3.如果没有return命令,函数将返回最后一个Shell的退出值。
1. 函数的调用
1.1 当前命令行调用
[root@localhost shell04]# cat fun1.sh 
#!/bin/bash
hello(){
echo "hello lilei $1"
hostname
}
menu(){
cat <<-EOF
1. mysql
2. web
3. app
4. exit
EOF
}

[root@localhost shell04]# source fun1.sh 
[root@localhost shell04]# . fun1.sh 

[root@localhost shell04]# hello 888
hello lilei 888
localhost.itcast.cc
[root@localhost shell04]# menu
1. mysql
2. web
3. app
4. exit


1.2 定义到用户的环境变量中

/etc/profile	/etc/bashrc		~/.bash_profile	~/.bashrc

[root@localhost shell04]# cat ~/.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 lilei $1"
hostname
}
menu(){
cat <<-EOF
1. mysql
2. web
3. app
4. exit
EOF
}

注意:
当用户打开bash的时候会读取该文件
1.3 脚本中调用
#!/bin/bash
#打印菜单
source ./fun1.sh
menu(){
cat <<-END
	h	显示命令帮助
	f	显示磁盘分区
	d	显示磁盘挂载
	m	查看内存使用
	u	查看系统负载
	q	退出程序
	END
}
menu		//调用函数

2. 应用案例

需求:写一个脚本让用户输入基本信息(姓名,性别,年龄),如不输入一直提示输入,最后根据用户的信息输出相对应的内容

思路:

  • 循环直到输入字符串不为空 -z -n
  • 根据用户输入信息做出匹配判断
#!/bin/bash
#该函数实现用户如果不输入内容则一直循环直到用户输入为止,并且将用户输入的内容打印出来
input_fun()
{
  input_var=""
  output_var=$1
  while [ -z $input_var ]
	do
	read -p "$output_var" input_var
	done
	echo $input_var
}


或者
fun(){
read -p "请输入您的姓名:" name
if [ -z $name ];then
fun
else
echo "你好,$name!"
fi
}
fun


#调用函数并且获取用户的姓名、性别、年龄分别赋值给name、sex、age变量
name=$(input_fun 请输入你的姓名:)
sex=$(input_fun 请输入你的性别:)
age=$(input_fun 请输入你的年龄:)

#根据用户输入的性别进行匹配判断
#根据用户所输入的内容进行判断输入
case $sex in
	man|)
	if [ $age -ge 18 -a $age -le 25 ];then
		echo "青少年,活力无限...,你可以茫然你也可以迷茫,但是要好好学习,不要挥霍浪费你的资本"
	elif [ $age -gt 25 -a $age -le 35 ];then
		echo "中年人,压力好大,但是家庭幸福的人儿"
	elif [ $age -lt 18 ];then
		echo "小伙子不错"
	else
		echo "$name先生,你油腻了吗?"

	fi
	;;
	woman|)
	echo "$name 小姐姐你好漂亮"
	;;
	*)
	echo "你是泰国来的吗?"
	;;

esac

	

三、综合案例

任务/背景:

现有的跳板机虽然实现了统一入口来访问生产服务器,yunwei用户权限太大可以操作跳板机上的所有目录文件,存在数据被误删的安全隐患,所以希望你做一些安全策略来保证跳板机的正常使用。

具体要求:

  1. 只允许yunwei用户通过跳板机远程连接后台的应用服务器做一些维护操作
  2. 公司运维人员远程通过yunwei用户连接跳板机时,跳出以下菜单供选择:
欢迎使用Jumper-server,请选择你要操作的主机:
1. DB1-Master
2. DB2-Slave
3. Web1
4. Web2
q. exit
  1. 当用户选择相应主机后,直接免密码登录成功
  2. 如果用户不输入一直提示用户输入,直到用户选择退出

思路:

  1. 需要当yunwei用户登录时执行一个脚本(该脚本放到哪里?)yunwei用户的家目录里的.bashrc
  2. 脚本中需要打印一个菜单供用户选择(case…esac)
  3. 免密码登录(昨天推公钥的脚本)
#!/bin/bash
#公钥推送成功

#!/bin/bash
#公钥推送成功
trap '' 1 2 3 19
#打印菜单用户选择
menu(){
cat <<-EOF
欢迎使用Jumper-server,请选择你要操作的主机:
1. DB1-Master
2. DB2-Slave
3. Web1
4. Web2
h. help
q. exit
EOF
}

#调用函数来打印菜单
menu
while true
do
read -p "请输入你要选择的主机[h for help]:" host

#通过case语句来匹配用户所输入的主机
case $host in
	1|DB1)
	ssh root@10.1.1.1
	;;
	2|DB2)
	ssh root@10.1.1.2
	;;
	3|web1)
	ssh root@10.1.1.250
	;;
	h|help)
	clear;menu
	;;
	q|quit)
	exit
	;;
esac
done


#!/bin/bash
#jumper-server
#菜单打印
trap '' 1 2 3 
while true
do
cat <<-END
欢迎使用Jumper-server,请选择你要操作的主机:
1. DB1-Master
2. DB2-Slave
3. Web1
4. Web2
5. exit
END
#让用户选择相应的操作
read -p "请输入你要操作的主机:" host
case $host in
	1)
	ssh root@10.1.1.2
	;;
	2)
	ssh root@10.1.1.3
	;;
	3)
	ssh root@10.1.1.4
	;;
	5)
	exit
	;;
	*)
	clear
	echo "输入错误,请重新输入..."
	;;
esac
done

自己完善功能:
1. 用户选择主机后,需要事先推送公钥;如何判断公钥是否已推
2. 比如选择web1时,再次提示需要做的操作,比如:
clean log
重启服务
kill某个进程


补充:

1) SIGHUP 		重新加载配置
2) SIGINT		键盘中断^C
3) SIGQUIT      键盘退出
9) SIGKILL		 强制终止
15) SIGTERM	    终止(正常结束),缺省信号
18) SIGCONT	   继续
19) SIGSTOP	   停止
20) SIGTSTP     暂停^Z

四、正则表达式

1. 什么是正则表达式

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

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

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

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

2. 第一类正则
  • 名词解释:

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

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

  • 正则中常用的元字符

示例文本:

[root@server ~]# cat 1.txt
ggle
gogle
google
gooogle
goooooogle
gooooooogle
taobao.com
taotaobaobao.com

jingdong.com
dingdingdongdong.com
10.1.1.1
Adfjd8789JHfdsdf/
a87fdjfkdLKJK
7kdjfd989KJK;
bSKJjkksdjf878.
cidufKJHJ6576,

hello world
helloworld yourself
(1).		任意单个字符,除了换行符 
(2)*		前导字符出现0次或连续多次  ab*能匹配“a”,“ab”以及“abb”,但是不匹配“cb”
grep abcd*  1.txt		abc  abcd  abcdd abcddddd

(3).*		任意长度的字符
(4)^		行的开头
(5)$		行的结尾
(6)^$		空行


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

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



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

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

 (17) \(\)	保存被匹配的字符


将192.168.0.254 换成 192.168.1.254
vim 1.txt
:%s#\(192\.168\)\.0\.\(254\)#\1\.100\.\2	//底行模式下匹配
将10.1.1.1替换成10.1.1.254
:%s#\(10.1.1\).1#\1.254#g 
:%s/\(10.1.1\).1/\1.254/g 

# sed -n 's#\(192\.168\)\.0\.254#\1\.1\.254#p'      
找出含有192.168的行,同时保留192.168并标记为标签1,之后可以使用\1来引用它。最多可以定义9个标签,从左边开始编号,最左边的是第一个。

[root@server shell05]# sed -n 's#10.1.1.1#10.1.1.254#p' 1.txt
10.1.1.254
[root@server shell05]# sed -n 's#\(10.1.1\).1#\1.254#p' 1.txt
10.1.1.254

将helloworld yourself 换成hellolilei myself

vim 1.txt
:%s#\(hello\)world your\(self\)#\1lilei my\2#g

# sed -n 's/\(hello\)world your\(self\)/\1lilei my\2/p' 1.txt 
hellolilei myself

[root@server shell05]# sed -n 's/helloworld yourself/hellolilei myself/p' 1.txt 
hellolilei myself
[root@server shell05]# sed -n 's/\(hello\)world your\(self\)/\1lilei my\2/p' 1.txt 
hellolilei myself


[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


  • 扩展类的正则表达式 grep -E 或则 egrep
扩展正则表达式元字符
+			匹配一个或多个前导字符		bo+	boo bo
?			匹配零个或一个前导字符		bo?	b bo
a|b		    匹配a或b
()			组字符	   hello myself yourself     (my|your)self

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

# grep "root|ftp|adm" /etc/passwd
# egrep "root|ftp|adm" /etc/passwd
# grep -E "root|ftp|adm" /etc/passwd

# grep -E 'o+gle' test.txt 
# grep -E 'o?gle' test.txt 

# egrep 'go{2,}' 1.txt
# egrep '(my|your)self' 1.txt


#### 3. 第二类正则

| 表达式    | 功能                             | 示例            |
| --------- | -------------------------------- | --------------- |
| [:alnum:] | 字母与数字字符                   | [[:alnum:]]+    |
| [:alpha:] | 字母字符(包括大小写字母)         | [[:alpha:]]{4}  |
| [:blank:] | 空格与制表符                     | [[:blank:]]*    |
| [:digit:] | 数字                             | [[:digit:]]?    |
| [:lower:] | 小写字母                         | [[:lower:]]{4,} |
| [:upper:] | 大写字母                         | [[:upper:]]+    |
| [:punct:] | 标点符号                         | [[:punct:]]     |
| [:space:] | 包括换行符,回车等在内的所有空白 | [[:space:]]+    |

~~~powershell
[root@server shell05]# grep -E '^[[:digit:]]+' 1.txt
[root@server shell05]# grep -E '^[^[:digit:]]+' 1.txt
[root@server shell05]# grep -E '[[:lower:]]{4,}' 1.txt
4. 课堂作业
在自己虚拟机里创建如下内容的文件:
# cat test.txt 
Aieur45869Root0000
9h847RkjfkIIIhello
rootHllow88000dfjj
8ikuioerhfhupliooking
hello world
192.168.0.254
welcome to uplooking.
abcderfkdjfkdtest
rlllA899kdfkdfj
iiiA848890ldkfjdkfj
abc
12345678908374
123456@qq.com
123456@163.com
abcdefg@itcast.com23ed

要求如下:

1、查找不以大写字母开头的行(三种写法)。
2、查找有数字的行(两种写法)
3、查找一个数字和一个字母连起来的
4、查找不以r开头的行
5、查找以数字开头的
6、查找以大写字母开头的 
7、查找以小写字母开头的 
8、查找以点结束的
9、去掉空行
10、查找完全匹配abc的行
11、查找A后有三个数字的行
12、统计root在/etc/passwd里出现了几次
13、用正则表达式找出自己的IP地址、广播地址、子网掩码

ifconfig eth0|grep Bcast|grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'

# egrep --color '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-eth0
IPADDR=10.1.1.1
NETMASK=255.255.255.0
GATEWAY=10.1.1.254

# egrep --color '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}' /etc/sysconfig/network-scripts/ifcfg-eth0 
IPADDR=10.1.1.1
NETMASK=255.255.255.0
GATEWAY=10.1.1.254

14、找出文件中的ip地址并且打印替换成172.16.2.254
15、找出文件中的ip地址
16、找出全部是数字的行
grep -E '^[0-9]+$' test
17、找出邮箱地址
grep -E '^[0-9]+@[a-z0-9]+\.[a-z]+$'

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         匹配整个单词
  

本文地址:https:///news/9_657.html/news/9_27788.html