Shell编程

第十一讲 Shell编程
11.1 基础正则表达式 
1、正则表达式与通配符
u正则表达式用来在文件中匹配符合条件的 字符串,正则是包含匹配。grep、awk、sed等命令可以支持正则表达式。 
u通配符用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp这些命令不 支持正则表达式,所以只能使用shell自己 的通配符来进行匹配了。
 
2、基础正则表达式
 
“*”前一个字符匹配0次,或任意多次  
ugrep "a*" test_rule.txt  
#匹配所有内容,包括空白行
ugrep "aa*" test_rule.txt  #匹配至少包含有一个a的行
ugrep "aaa*" test_rule.txt  #匹配最少包含两个连续a的字符串
ugrep "aaaaa*" test_rule.txt  #则会匹配最少包含四个个连续a的字符串
 
“.” 匹配除了换行符外任意一个字符
ugrep  "s..d" test_rule.txt  #“s..d”会匹配在s和d这两个字母之间一定有两个字符的单词 
ugrep "s.*d" test_rule.txt  #匹配在s和d字母之间有任意字符
ugrep ".*" test_rule.txt #匹配所有内容
 
“^”匹配行首,“$”匹配行尾
ugrep "^M" test_rule.txt  #匹配以大写“M”开头的行  
ugrep "n$" test_rule.txt  #匹配以小写“n”结尾的行  
ugrep  -n  "^$" test_rule.txt  #会匹配空白行
 
“[]” 匹配中括号中指定的任意一个 字符,只匹配一个字符
ugrep "s[ao]id" test_rule.txt #匹配s和i字母中,要不是a、要不是o  
ugrep "[0-9]" test_rule.txt #匹配任意一个数字
ugrep "^[a-z]" test_rule.txt  #匹配用小写字母开头的行
 
“[^]” 匹配除中括号的字符以外的 任意一个字符
ugrep "^[^a-z]" test_rule.txt   #匹配不用小写字母开头的行  
ugrep "^[^a-zA-Z]" test_rule.txt  #匹配不用字母开头的行
 
“\” 转义符
ugrep "\.$" test_rule.txt   #匹配使用“.”结尾的行
“\{n\}”表示其前面的字符恰好出现n次
ugrep "a\{3\}" test_rule.txt  #匹配a字母连续出现三次的字符串  
ugrep "[0-9]\{3\}" test_rule.txt  #匹配包含连续的三个数字的字符串
“\{n,\}”表示其前面的字符出现不小于n次  
ugrep "^[0-9]\{3,\}[a-z]" test_rule.txt   #匹配最少用连续三个数字开头的行
“\{n,m\}”匹配其前面的字符至少出现n次, 最多出现m次
ugrep "sa\{1,3\}i" test_rule.txt   #匹配在字母s和字母i之间有最少一个a,最多三个a
11.2 字符截取命令
11.2.1  cut字段提取命令 (grep是提取符合条件的行)
[root@localhost ~]# cut [选项] 文件名
    选项:
     -f 列号: 提取第几列
  -d 分隔符: 按照指定分隔符分割列(默认使用的是制表符tab)
[root@localhost ~]# vi student.txt 
 ID      Name    gender  Mark
   1        Liming  M         86
   2        Sc          M         90
   3        Gao        M         83 
[root@localhost ~]# cut -f 2 student.txt
[root@localhost ~]# cut -f  2,3 student.txt
[root@localhost ~]# cut -d ":" -f 1,3  /etc/passwd 
 
cut命令的局限(空格符)
[root@localhost ~]# df -h | cut -d " " -f 1,3
11.2.2  printf命令
printf  ’输出类型输出格式’    输出内容
输出类型:
 %ns: 输出字符串。n是数字指代输出几个字符 
 %ni: 输出整数。n是数字指代输出几个数字 
 %m.nf: 输出浮点数。m和n是数字,指代输出的整数位数和小数位数。如%8.2f代表共输出8位数,其中2位是小数,6位是整数。
 
输出格式:
 \a: 输出警告声音
 \b: 输出退格键,也就是Backspace键 
 \f: 清除屏幕
 \n: 换行
 \r: 回车,也就是Enter键
 \t: 水平输出退格键,也就是Tab键 
 \v: 垂直输出退格键,也就是Tab键
 
[root@localhost ~]# printf %s 1 2 3 4 5 6 
[root@localhost ~]# printf %s %s %s 1 2 3 4 5 6
[root@localhost ~]# printf  '%s %s %s' 1 2 3 4 5 6  #表示每三个为一组
[root@localhost ~]# printf '%s %s %s\n' 1 2 3 4 5 6
[root@localhost ~]# vi student.txt 
ID      Name      PHP     Linux   MySQL   Average  
1       Liming     82         95         86            87.66
2       Sc             74         96         87            85.66
3       Gao           99         83         93           91.66
printf  '%s' $(cat student.txt)  #不调整输出格式
printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat student.txt)  #调整格式输出
在awk命令的输出中支持print和printf命令  
uprint:print会在每个输出之后自动加入一个换行符(Linux默认没有print命令)  
uprintf:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,需要 手工加入换行符
 
11.2.3  awk(编程)命令(包含cut的所有功能,先读入,在执行)
# awk ‘条件1{动作1} 条件2{动作2}…’ 文件名  
条件(Pattern):
 一般使用关系表达式作为条件
 x > 10 判断变量 x是否大于10
 x>=10 大于等于
 x<=10 小于等于
动作(Action):
 格式化输出
 流程控制语句
[root@localhost ~]# vi student.txt 
ID      Name      PHP     Linux   MySQL   Average  
 1        Liming    82        95         86             87.66
2        Sc            74        96         87             85.66
3        Gao          99        83        93              91.66
# awk '{printf $2 "\t" $6 "\n"}' student.txt   #输出第2,6列,一行一行的读取
# df -h | awk '{print $1 "\t" $3}'
BEGIN
# awk 'BEGIN{printf "This is a transcript \n" } {printf $2 "\t" $6 "\n"}' student.txt  
END
# awk 'END{printf "The End \n" }                              
 {printf $2 "\t" $6 "\n"}' student.txt
 
FS内置变量
# cat /etc/passwd | grep "/bin/bash" | \
awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\n"}' 
关系运算符
# cat student.txt | grep -v Name |  \  awk '$6 >= 87 {printf $2 "\n" }'
11.2.4  sed命令
used 是一种几乎包括在所有 UNIX 平台( 包括 Linux)的轻量级流编辑器。sed主要是用来将数据进行选取、替换、删除、新 增的命令。
[root@localhost ~]# sed [选项] ‘[动作]’ 文件名  选项:
 -n: 一般sed命令会把所有数据都输出到屏幕 ,如果加入此选择,则只会把经过sed命令处
 理的行输出到屏幕。
 -e: 允许对输入数据应用多条sed命令编辑
 -i: 用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出
动作:
 a \: 追加,在当前行后添加一行或多行。添加多行时,除最后 一行外,每行末尾需要用“\”代表数据未完结。
 c \: 行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用“\”代表数据未完结。
 i \: 插入,在当期行前插入一行或多行。插入多行时,除最后 一行外,每行末尾需要用“\”代表数据未完结。
 d: 删除,删除指定的行。
 p: 打印,输出指定的行。
 s: 字串替换,用一个字符串替换另外一个字符串。格式为“行范围s/旧字串/新字串/g”(和vim中的替换格式类似)。
 
学生成绩表
[root@localhost ~]# vi student.txt 
ID      Name      PHP     Linux   MySQL   Average  
 1        Liming    82        95         86             87.66
2        Sc            74        96         87             85.66
3        Gao         99        83         93             91.66
行数据操作
[root@localhost ~]# sed '2p' student.txt   #查看文件的第二行
[root@localhost ~]# sed -n '2p' student.txt  
[root@localhost ~]# sed '2,4d' student.txt   #删除第二行到第四行的数据,但不修改文件本身
[root@localhost ~]# sed '2a hello' student.txt    #在第二行后追加hello
[root@localhost ~]# sed '2i  hello \  world' student.txt #在第二行前插入两行数据
# sed '2c No such person‘ student.txt  #数据替换 
字符串替换
# sed ‘s/旧字串/新字串/g’ 文件名
# sed '3s/74/99/g' student.txt   #在第三行中,把74换成99  
#sed -i '3s/74/99/g' student.txt   #sed操作的数据直接写入文件
# sed -e 's/Liming//g ; s/Gao//g' student.txt   #同时把“Liming”和“Gao”替换为空
11.3 字符处理命令
1、排序命令sort(默认按字母排序)
[root@localhost ~]# sort [选项] 文件名
选项:
 -f: 忽略大小写
 -n: 以数值型进行排序,默认使用字符串型排序 
 -r: 反向排序
 -t: 指定分隔符,默认是分隔符是制表符
 -k n[,m]: 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)
[root@localhost ~]# sort /etc/passwd  #排序用户信息文件
[root@localhost ~]# sort -r /etc/passwd  #反向排序
[root@localhost ~]# sort -t ":" -k 3,3 /etc/passwd 
#指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序
[root@localhost ~]# sort -n -t ":" -k 3,3 /etc/passwd
2、统计命令wc
[root@localhost ~]# wc [选项] 文件名  选项:
 -l: 只统计行数
 -w: 只统计单词数
-m: 只统计字符数
11.4 条件判断
1、按照文件类型进行判断
两种判断格式
[root@localhost ~]# test -e /root/install.log
[root@localhost ~]# [ -e /root/install.log ]  #两侧空格不能省略
[ -d /root ] && echo "yes" || echo "no"  
#第一个判断命令如果正确执行,则打印“yes”,否则打印“no” 
2、按照文件权限进行判断
[ -w student.txt ] && echo "yes" || echo "no"                   
#判断文件是拥有写权限的
3、两个文件之间进行比较
ln /root/student.txt /tmp/stu.txt  #创建个硬链接吧
[ /root/student.txt -ef /tmp/stu.txt ] && echo "yes" || echo "no"        
yes
#用test测试下,果然很有用
4、两个整数之间比较
[ 23 -ge 22 ] && echo "yes" || echo "no"                                  
yes
#判断23是否大于等于22,当然是了
[ 23 -le 22 ] && echo "yes" || echo "no"   no 
#判断23是否小于等于22,当然不是了
5、字符串的判断
name=sc
#给name变量赋值
[ -z "$name" ] && echo "yes" || echo "no"  
no
#判断name变量是否为空,因为不为空,所以返回no
 
aa=11
bb=22
#给变量aa和变量bb赋值
[ "$aa" == "bb" ] && echo "yes" || echo "no"            
no
#判断两个变量的值是否相等,明显不相等,所以返回no 
 
6、多重条件判断
aa=11
[ -n "$aa"  -a "$aa" -gt 23 ] && echo "yes" || echo "no"
no
#判断变量aa是否有值,同时判断变量aa的是否大于23
#因为变量aa的值不大于23,所以虽然第一个判断值为真,返回的结果也是假 
 
aa=24
[ -n "$aa"  -a "$aa" -gt 23 ] && echo "yes" || echo "no"
yes
 
11.5 流程控制
 
11.5.1  if语句
1、 单分支if条件语句
if [ 条件判断式 ];then 
  程序
fi
或者
if [ 条件判断式 ]
 then
     程序
fi
 
单分支条件语句需要注意几个点:
uif语句使用fi结尾,和一般语言使用大括 号结尾不同
u[ 条件判断式 ]就是使用test命令判断,所 以中括号和条件判断式之间必须有空格
uthen后面跟符合条件之后执行的程序,可 以放在[]之后,用“;”分割。也可以换 行写入,就不需要“;”了
 
例子:判断分区使用率
#!/bin/bash
#统计根分区使用率
# Author: shenchao (E-mail: shenchao@lampbrother.net
 rate=$(df -h | grep "/dev/sda3" | awk '{print $5}' | cut -d "%" -f 1)
#把根分区使用率作为变量值赋予变量rate
 if [ $rate -ge 80 ]
 then
    echo "Warning!/dev/sda3 is full!!" 
 fi
 
2、双分支if条件语句
if [ 条件判断式 ]
 then
 条件成立时,执行的程序
 else 
 条件不成立时,执行的另一个程序 
fi
 
例子1:备份mysql数据库
#!/bin/bash
#备份mysql数据库。
# Author: shenchao (E-mail: shenchao@lampbrother.net
 
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 "Data 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 "Data 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 
fi
 
例子2:判断apache是否启动
#!/bin/bash
# Author: shenchao (E-mail: shenchao@lampbrother.net
 
port=$(nmap -sT 192.168.1.156 | grep tcp | grep http | awk '{print $2}') 
#使用nmap命令扫描服务器,并截取apache服务的状态,赋予变量port
if [ "$port" == "open" ]
 then
 echo “$(date) httpd is ok!” >> /tmp/autostart-acc.log 
 else
        /etc/rc.d/init.d/httpd start &>/dev/null
 echo "$(date) restart httpd !!" >> /tmp/autostart-err.log 
 fi
 
3、 多分支if条件语句
if [ 条件判断式1 ]
 then 
 当条件判断式1成立时,执行程序1 
elif [ 条件判断式2 ]
 then 
 当条件判断式2成立时,执行程序2 
„省略更多条件…
else 
 当所有条件都不成立时,最后执行此程序  
fi
 
#!/bin/bash
#判断用户输入的是什么文件
# Author: shenchao (E-mail: shenchao@lampbrother.net
 
read -p "Please input a filename: " file  
#接收键盘的输入,并赋予变量file
 
if [ -z "$file" ]
#判断file变量是否为空
 then
        echo "Error,please input a filename" 
        exit 1
elif [ ! -e "$file" ]
#判断file的值是否存在
 then
        echo "Your input is not a file!" 
        exit 2
elif [ -f "$file" ]
#判断file的值是否为普通文件
 then
        echo "$file is a regulare file!"  elif [ -d "$file" ]
#判断file的值是否为目录文件
 then
        echo "$file is a directory!"
else
        echo "$file is an other file!"
fi
 
 
11.5.2  case语句
多分支case条件语句
ucase语句和if…elif…else语句一样都是多 分支条件语句,不过和if多分支条件语句
不同的是,case语句只能判断一种条件关 系,而if语句可以判断多种条件关系。
 
case $变量名 in
 "值1") 
     如果变量的值等于值1,则执行程序1
     ;;
 "值2") 
     如果变量的值等于值2,则执行程序2
     ;;
 …省略其他分支…
 *) 
     如果变量的值都不是以上的值,则执行此程序 
     ;;
esac
 
 
#!/bin/bash
#判断用户输入
# Author: shenchao (E-mail: shenchao@lampbrother.net
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
 
 
11.5.3  for循环
语法一(in几个值循环几次,每次循环把值赋给变量,然后执行程序)
for 变量 in 值1 值2 值3… 
 do
 程序
 done
 
#!/bin/bash
#打印时间
# Author: shenchao (E-mail: shenchao@lampbrother.net
 
for time in morning noon afternoon evening 
        do
                echo "This time is $time!" 
        done
 
 
#!/bin/bash
#批量解压缩脚本
# Author: shenchao (E-mail: shenchao@lampbrother.net
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
 
 
语法二
 
for (( 初始值;循环控制条件;变量变化 )) 
 do
 程序
 done
 
 
#!/bin/bash
#从1加到100
# Author: shenchao (E-mail: shenchao@lampbrother.net
 s=0
for (( i=1;i<=100;i=i+1 ))
        do
                s=$(( $s+$i ))
        done
echo "The sum of 1+2+...+100 is : $s"
 
 
#!/bin/bash
#批量添加指定数量的用户
# Author: shenchao (E-mail: shenchao@lampbrother.net
read -p "Please input user name: " -t 30 name
read -p "Please input the number of users: " -t 30 num 
read -p "Please input the password of users: " -t 30 pass
if [ ! -z "$name" -a ! -z "$num" -a ! -z "$pass" ]
        then
        y=$(echo $num | sed 's/[0-9]//g')
 if [ -z "$y" ]
                then
                for (( i=1;i<=$num;i=i+1 ))
                    do  
                        /usr/sbin/useradd $name$i &>/dev/null 
 echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null 
 done
        fi  
fi
 
 
11.5.4  while循环与until循环
1、while循环
uwhile循环是不定循环,也称作条件循环 。只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停 止。这就和for的固定循环不太一样了。
while [ 条件判断式 ] 
 do
    程序
 done
 
 
#!/bin/bash
#从1加到100
# Author: shenchao (E-mail: shenchao@lampbrother.net
 
i=1 
s=0 
while [ $i -le 100]
#如果变量i的值小于等于100,则执行循环 
        do 
                s=$(( $s+$i )) 
                i=$(( $i+1 )) 
        done
 
echo "The sum is: $s"
 
2、until循环
uuntil循环,和while循环相反,until循环时 只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。
until [ 条件判断式 ] 
 do
    程序
 done
 
 
#!/bin/bash
#从1加到100
# Author: shenchao (E-mail: shenchao@lampbrother.net
 
i=1 
s=0 
until [ $i -gt 100 ]
#循环直到变量i的值大于100,就停止循环 
        do 
                s=$(( $s+$i )) 
                i=$(( $i+1 )) 
        done
echo "The sum is: $s"
posted @ 2019-03-13 22:55  白開水  阅读(190)  评论(0编辑  收藏  举报