第十一讲 Shell编程
一、正则表达式
1.1 正则表达式与通配符
a)正则表达式用来在文件中匹配符合条件的字符串,正则是【包含匹配】。grep、awak、sed等
命令可以支持正则表达式。
b)通配符用来匹配符合条件的文件名,通配符是【完全匹配】。ls、find、cp这些命令不支持正则
表达式,所以只能使用shell自己的通配符进行匹配。
1.2 基础正则表达式
元字符 |
作用 |
* |
前一个字符匹配0次或任意多次 |
. |
匹配除了换行符外任意一个字符 |
^ |
匹配行首。如:^hello会匹配以hello开头的行 |
$ |
匹配行尾。如:hello$会匹配以hello结尾的行 |
[] |
匹配括号中指定的任意一个字符,只匹配一个字符。例如[aoeiu]匹配任意一个元音字母,
[0-9]匹配任意一位数字,[a-z][0-9]匹配小写字母和一位数字组成的两位字符
|
[^] |
匹配除括号中的字符以外的任意一个字符。如[^0-9]匹配任意一位非数字字符,[^a-z]
表示任意一位非小写字母
|
\ |
转义符。用于取消特殊符号的含义 |
\{n\} |
表示其前面的字符恰好出现n次,例如:[0-9]\{4\}匹配4位数字,[1][3-8][0-9]{9\}
匹配手机号码
|
\{n,\} |
表示其前面的字符出现不小于n次,例如:[0-9]\{2,\}表示两位及以上的数字
|
\{n,m\} |
表示其前面的字符至少出现n次,最多出现m次,例如[a-z]\{6,8\}匹配6到8位小写字母
|
例如:
”*“ 前一个字符匹配0次或任意多次
grep "a*" test_rule.txt #表示匹配所有内容,包含空白行(无意义)
grep "aa*" test_rule.txt #表示匹配至少包含一个a的行
grep "aaa*" test_rule.txt #表示匹配包含两个连续a的字符串
"." 除了换行符外任意一个字符
grep "s..d" test_rule.txt #会匹配在s和d两个字符之间有两个字符的单词
grep "s.*d" test_rule.txt #会匹配在s和d两个字符之间有任意字符
grep ".*" #匹配所有内容
"^" 匹配行首,”$“匹配行尾
grep "^M" test_rule.txt #匹配以大写字母M开头的行
grep "n$" test_rule.txt #匹配以n结尾的行
grep -n "^$" test_rule.txt #匹配空白行
"[]" 匹配括号中指定的任意一个字符,只匹配一个字符
grep "s[ao]id" test_rule.txt #匹配s和i字母中,要么是a,要么是o
grep "[0-9]" test_rule.txt #匹配任意一个数字
grep "^[a-z]" test_rule #匹配用小写字母开头的行
grep "^[^#]" test_rule #匹配不以 #开头的行,可以有效过滤注释
"[^]" 匹配除括号中的字符以外的任意一个字符
grep "^[^a-z]" test_rule.txt #匹配不以小写字母开头的行
grep "^[^a-zA-Z]" test_rule.txt #匹配不以字母开头的行
"\" 转义符
grep "\.$" test_rule.txt #匹配以"."结尾的行
"\{n\}" 表示前面的字符恰好出现n次
grep "a\{3\}" test_rule.txt #匹配a字母连续出现三次的字符串
grep "[0-9]\{3\}" test_rule.txt #匹配包含连续的三个数字的字符串
"\{n,\}" 表示其前面的字符出现不小于n次
grep "^[0-9]\{3,\}[a-z]" test_rule.txt #表示最少用连续三个数字开头的行
"\{n,m\}" 表示前面的字符至少出现n次,最多出现m次
grep "sa\{1,3\}i" test_rule.txt #匹配在字母s和字母i之间最少有一个a,最多有三个a
二、字符截取命令
2.1 cut字段提取命令
命令格式:cut [选项] 文件名
选线:
-f 列号: 提取第几列(默认按制表符分隔)
-d 分隔符: 按照指定分隔符分隔列
例如:
cut -d ":" -f 1,3 /etc/passwd #以":"分隔,提取第1列和第3列
注意:cut命令一般和grep命令结合使用,如
cat /etc/passwd | grep /bin/bash | grep -v root | cut -d ":" -f 1
#此语句表示查找不为用户名不为root的所有可登录系统用户的用户名
cut 命令的局限
如果以空格为分隔符,cut命令不会正确输出想要的结果,此时用awk更合适
2.2 printf命令
命令格式:printf ‘输出类型输出格式’ 输出内容
输出类型:
%ns 输出字符串,n是数字指代输出几个字符
%ni 输出整数,n是数字指代输出几个数字
%m.nf 输出浮点数,m和n是数字,指代输出的整数位和小数位数,如
%8.2f代表共输出8位数,其中2位是小数,6位是整数
输出格式:
\a 输出警告声音
\b 输出退格键
\f 清除屏幕
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
printf '%s' $(cat student.txt)
注意:在awk命令的输出中支持print和printf命令
print:print会在每个输出后自动加入一个换行符(Linux默认没有print命令)
printf:printf是标准格式输出命令,不会自动加入换行符,需手动加入
2.3 awk命令
命令格式:awk '条件1{动作1} 条件2{动作2} ...' 文件名
条件:一般用关系表达式作为条件
x>10 判断变量x是否大于10
x>=10 大于等于
x<=10 小于等于
动作:
格式化输出
流程控制语句
例如:
awk '{printf $2 "\t" $6 '\n'}' student.txt #表示打印第2列和第6列的数据
df -h | awk '{print $1 "\t" $3 }' 解决cut缺陷
BEGIN
awk 'BEGIN {printf "This is a transcript \n"}' #处理第一行数据
{printf $2 "\t" $6 "\n"} student.txt
ES内置变量
cat /etc/passwd | grep "/bin/bash" | awk 'BEGIN {FS=";"} {printf $1 "\t" $3 "\n"}'
END
awk 'END{printf "The End \n"} {printf $2 "\t" $4 "\n"}' student.txt
关系运算符
cat student.txt | grep -v Name | awk '$4 > 87 {print $2}' #第一行有Name的行需要剔除掉
2.4 sed命令
2.4.1 简介
sed 是一种几乎包括在所有UNIX平台(包括Linux)的轻量级流编辑器。sed主要是用来将数据进行
选取、替换、删除、新增的命令。优点:支持管道符操作
2.4.2 命令格式
sed [选项] '[动作]' 文件名
选项:
-n 将经过sed处理的行输出到屏幕
-e 允许对输入数据应用多条sed命令编辑
-i 用sed的修改结果直接修改读取数据的文件,而非屏幕输出
动作:
a\: 追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾都需要用"\"代表未完结
c\: 行替换,用c后面的字符串替换源数据行,替换多行时,除最后一行外,每行末尾用”\“代表未完结
i\: 插入,在当前行后插入一行或多行。插入多行时,除最后一行外,每行末尾都需要用"\"代表未完结
d: 删除指定的行
p: 打印输出指定的行
s: 字串替换,用一个字符串替换另一个字符串,格式为"行范围s/旧字串/新字串/g"(类似于vim)。
2.4.3 行数据操作
sed '2p' student.txt #查看文件第二行(所有行都输出,第二行多输出了一次)
sed -n '2p' student.txt #只查看第二行
sed '2,4d' student.txt #删除第二行到第四行的数据,但不修改文件本身
sed '2a hello' student.txt #在第二行追加hello
sed '2i hello \
world' student.txt #在第二行前插入两行数据
sed '2c No such person' student.txt #第二行数据被替换
sed '3s /80/90/g' student.txt #在第三行中,将80替换成99
sed -i '3s/80/90/g' student.txt #sed操作的数据直接写入文件
sed -e 's/LIming//g;s/Gao//g' #同时将"Liming"和"Gao"替换为空
三、字符处理命令
3.1 排序命令 sort
命令格式:sort [选项] 文件名
选项:
-f 忽略大小写
-n 以数值型进行排序,默认使用字符串型排序
-r 反向排序
-t 指定分隔符,默认分隔符是制表符
-k n[,m] 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)
例如:
sort /etc/passwd #排序用户信息文件
sort -r /etc/passwd #反向排序
sort -t ":" -k 3,3 /etc/passwd #指定分隔符":" 用第二字段开头,第三字段结尾排序,就是只用第三字段排序
sort -n -t ":" -k 3,3 /etc/passwd #使用数值型的第三字段进行排序
3.2 统计命令wc
命令格式:wc [选项] 文件名
选项:
-i 只统计行数
-w 只统计单词数
-m 只统计字符数
四、条件判断
4.1 按文件类型进行判断
测试选项 |
作用 |
-b 文件 |
判断该文件是否存在,并且是否为块设备文件 |
-c 文件 |
判断该文件是否存在,并且是否为字符设备文件 |
-d 文件 |
判断该文件是否存在,并且是否为目录文件 |
-e 文件 |
判断该文件是否存在 |
-f 文件 |
判断该文件是否存在,并且是否为普通文件 |
-L 文件 |
判断该文件是否存在,并且是否为符号链接文件 |
-p 文件 |
判断该文件是否存在,并且是否为管道文件 |
-s 文件 |
判断该文件是否存在,并且是否为非空 |
-S 文件 |
判断该文件是否存在,并且是否为套接字文件 |
两种判断格式
a)test -e /root/install.log
b)[ -e /root/install.log ]
注意:此命令无返回结果,需要用配合$? 判断上一条命令是否执行正确
或者使用 [ -d /root ] && echo "yes" || echo "no" #第一个命令执行正确,打印yes,否则打印no
4.2 按照文件权限判断
测试选项 |
作用 |
-r 文件 |
判断文件是否存在,并且是否该文件拥有读权限 |
-w 文件 |
判断文件是否存在,并且是否改文件拥有写权限 |
-x 文件 |
判断文件是否存在,并且是否改文件拥有执行权限 |
-u 文件 |
判断文件是否存在,并且是否改文件拥有SUID权限 |
-g 文件 |
判断文件是否存在,并且是否改文件拥有SGID权限 |
-k 文件 |
判断文件是否存在,并且是否该文件拥有Sbit权限 |
4.3 两个文件之间进行比较
测试选项 |
作用 |
文件1 -nt 文件2 |
判断文件1的修改时间是否比文件2的新 |
文件1 -ot 文件2 |
判断文件1的修改时间是否比文件2的旧 |
文件1 -ef 文件2 |
判断两个文件Inode是否一致,即是否为同一个文件(判断硬链接) |
例如:
ln /root/student.txt /tmp/stu.txt #创建硬链接
[ /root/student.txt -ef /tmp/stu.txt ] && echo "yes" || echo "no" #判断两个文件是否为同一个
4.4 两个数值之间比较
测试选项 |
作用 |
-eq |
判断是否相等 |
-ne |
判断是否不相等 |
-gt |
判断是否大于 |
-lt |
判断是否小于 |
-ge |
判断是否大于等于 |
-le |
判断是否小于等于 |
例如:
[ 23 -gt 22 ] && echo "yes" || echo "no" #判断23是否大于22
4.5 字符串的判断
测试选项 |
作用 |
-z 字符串 |
判断是否为空 |
-n 字符串 |
判断是否为非空 |
字串1 == 字串2 |
判断是否相等 |
字串1 != 字串2 |
判断是否不相等 |
例如:
name=test #赋值变量name
[ -z "$name" ] && echo "yes" || echo "no" #判断变量是否为空
aa=11
bb=22
#给两个变量赋值
[ "$aa" == "$bb" ] && echo "yes" || echo "no" #判断这两个变量是否相等
4.6 多重条件判断
测试选项 |
作用 |
判断1 -a 判断2 |
逻辑与,判断1和判断2都为真,结果为真 |
判断1 -o 判断2 |
逻辑或,判断1和判断2有一个为真,结果为真 |
! 判断 |
逻辑非,原始判断取反 |
例如:
aa=24 #变量赋值
[ -n "$aa" -a "$aa" -gt 23 ] && echo "yes" || echo "no" # 判断变量不为空且aa大于23是打印yes
五、流程控制
5.1 if语句
5.1.1 单分支if条件语句
if [ 条件判断 ] ;then
程序
fi
或者
if [ 条件判断 ]
then
程序
fi
注意:
a)if语句结尾用fi
b)[ 条件判断 ]就是test命令判断,中括号和条件判断式之间必须要有空格
c)then后面跟符合条件之后执行程序。和if同一行时需要加";"号
例子:判断分区使用率(注意:rate赋值时,等号前后无空格)
#!/bin/bash
rate=$(df -h | grep "/dev/sda1" | awk '{print $5}' | cut -d "%" -f 1) #将逻辑分区使用率作为变量赋值给rate
if [ $rate -ge 80]
then
echo "Warning! /dev/sda1 is full !!"
fi
5.1.2 双分支if条件语句
if [ 条件判断式 ]
then
条件成立时,执行的程序
else
条件不成立时,执行的程序
fi
例子1:备份mysql数据库
#!/bin/bash
ntpdate asia.pool.ntp.org &> /dev/null #同步系统时间
date=$(date +%y%m%d) #将当前系统时间按照"年月日格式赋予变量date"
size=$(du -sh /var/lib/mysql) #统计mysql数据库的大小,并把大小赋予size变量
if [ -d /tmp/dbbak ]
then
echo "Date:$date!" > /tmp/dbbak/dbinfo.txt
echo "Date size:$size" >> /tmp/dbbak/dbinfo.txt
cd /tmp/dbbak
tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null
rm -rf /tmp/dbbak/dbinfo.txt
else
mkdir /tmp/dbbak
echo "Date:$date!" > /tmp/dbbak/dbinfo.txt
echo "Date size:$size" >> /tmp/dbbak/dbinfo.txt
cd /tmp/dbbak
tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null
rm -rf /tmp/dbbak/dbinfo.txt
例子2:判断apache是否启动
#!/bin/bash
port=$(nmap -sT 192.168.1.10 | grep tcp | grep http | awk '{print $2}')
#namp命令扫描服务器,截取apache服务的状态,赋予变量port
if [ "$port" == "open" ]
then
echo "$(date) httpd is ok!!!" >> /tmp/autostart-acc.log
else
systemctl start httpd.service &> /dev/null
echo "$(date) restart httpd!!" >> /tmp/autostart-err.log
fi
5.1.3 多分支if条件语句
if [ 条件判断式1 ]
then
当条件判断式1成立时,执行程序1
exit 1
elif [ 条件判断式2]
then 当条件判断式2成立时,执行程序2
exit 2
......
else
当所有条件都不成立时,最后执行此程序
fi
5.2 case语句
5.2.1 多分支case条件语句
case语句和if...elif...else语句一样都是多分支条件语句,不过和if多分支条件语句不同的事
case只能判断一种条件关系,而if语句可以判断多种条件关系
5.2.2 格式
case $变量名 in
"值1")
如果变量的值等于值1,则执行程序1
;;
"值2")
如果变量的值等于值2,则执行程序2
;;
...
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
例子:
#!/bin/bash
read -p "Please choose yes/no?" -t 30 cho
case $cho in
"yes")
echo "Your choose is yes"
;;
"no")
echo "Your choose is no"
;;
*)
echo "Your choose is error"
;;
esac
5.3 for循环
5.3.1 语法一
for 变量 in 值1 值2 值3...
do
程序
done
例子1:循环打印1,2,3,4,5
#!/bin/bash
for i in 1 2 3 4 5
do
echo $i
done
例子2:批量解压缩脚本
cd /lamp
ls *.tar.gz > ls.log
for i in $(cat ls.log)
do
tar -zxf $i &>/dev/null
done
rm -rf /lamp/ls.log
5.3.2 语法2
for ((初始值;循环控制条件;变量变化))
do
程序
done
例子1:求1到100的和
#!/bin/bash
s=0
for((i=1;i<=100;i++))
do
s=$(($s + i))
done
echo "1~100 sum is $s"
例子2:批量添加指定数量的用户
#!/bin/bash
read -t 30 -p "input name:" name
read -t 30 -p "input num:" num
read -t 30 -p "input password:" password
if [ ! -z "$name" -a ! -z "$num" -a ! -z "$password" ]
then
y=$(echo $num | sed 's/[0-9]*$//g')
if [ -z $y ]
then
for ((i=1;i<=$num;i=i+1))
do
useradd $name$i &>/dev/null
echo $password | passwd --stdin $name$i &>/dev/null
done
fi
fi
5.4 while循环与until循环
5.4.1 while循环
while循环是不定循环,也称作条件循环。只要条件判断式成立,循环就会一直继续。
格式:while [ 条件判断式 ]
do
程序
done
例子:求1~100的和
#!/bin/bash
i=1
s=0
while [ $i -le 100 ]
do
s=$(($s+$i))
i=$(($i+1))
done
echo "1~100 sum is $s"
5.4.2 until循环
until循环,和while循环相反,只要条件判断式不成立则循环,一旦成立,则终止循环。
格式和while循环一致
例子:求1~100的和
#!/bin/bash
i=1
s=0
until [ $i -gt 100 ]
do
s=$(($s+$i))
i=$(($i+1))
done
echo "1~100 sum is $s"