第十一章 Shell编程

11.1 基础正则表达式

  1. 正则表达式与通配符
  • 正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配。grep、awk、sed等命令可以支持正则表达式。
  • 通配符用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配了。

2、基础正则表达式
【Linux学习笔记11】第十一章 Shell编程 - 图1
“*”前一个字符匹配0次,或任意多次

  • grep “a*” test_rule.txt

匹配所有内容,包括空白行

  • grep “aa*” test_rule.txt

匹配至少包含有一个a的行

  • grep “aaa*” test_rule.txt

匹配最少包含两个连续a的字符串

  • grep “aaaaa*” test_rule.txt

则会匹配最少包含四个个连续a的字符串

“.” 匹配除了换行符外任意一个字符

  • grep “s..d” test_rule.txt

“s..d”会匹配在s和d这两个字母之间一定有两个字符的单词

  • grep “s.*d” test_rule.txt

匹配在s和d字母之间有任意字符

  • grep “.*” test_rule.txt

匹配所有内容

“^”匹配行首,“$”匹配行尾

  • 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.txt

匹配用小写字母开头的行

“[^]” 匹配除中括号的字符以外的任意一个字符

  • grep “^[â-z]” test_rule.txt

匹配不用小写字母开头的行

  • grep “^[â-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

11.2 字符截取命令

11.2.1 cut字段提取命令

[root@localhost ~]# cut [选项] 文件名
选项:
-f 列号: 提取第几列
-d 分隔符: 按照指定分隔符分割列

[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命令

[root@localhost ~]# 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
tips:printf ‘%s %s %s\n’ 1 2 3 4 5 6
三个看成一个整体所以123是一个整体,456是另一个。所以在3后面换行
【Linux学习笔记11】第十一章 Shell编程 - 图2

[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命令

  • print:print会在每个输出之后自动加入一个换行符(Linux默认没有print命
  • printf:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,需要手工加入换行符

11.2.3 awk命令

[root@localhost ~]# 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

[root@localhost ~]#awk ‘{printf $2 “\t” $6 “\n”}’ student.txt
[root@localhost ~]# df -h | awk ‘{print $1 “\t” $3}’
【Linux学习笔记11】第十一章 Shell编程 - 图3
提取已用百分比
【Linux学习笔记11】第十一章 Shell编程 - 图4

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命令

sed命令

  • sed 是一种几乎包括在所有 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
#删除第二行到第四行的数据,但不修改文件本身
【Linux学习笔记11】第十一章 Shell编程 - 图5
【Linux学习笔记11】第十一章 Shell编程 - 图6

[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 字符处理命令

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


11.3.2 统计命令wc

[root@localhost ~]# wc [选项] 文件名
选项:
-l: 只统计行数
-w: 只统计单词数
-m: 只统计字符数

11.4 条件判断

11.4.1 按照文件类型进行判断

【Linux学习笔记11】第十一章 Shell编程 - 图7
两种判断格式
[root@localhost ~]# test -e /root/install.log
[root@localhost ~]# [ -e /root/install.log ]
[ -d /root ] && echo “yes” || echo “no”
#第一个判断命令如果正确执行,则打印“yes”,否则打印“no”
【Linux学习笔记11】第十一章 Shell编程 - 图8

11.4.2 按照文件权限进行判断

【Linux学习笔记11】第十一章 Shell编程 - 图9
[ -w student.txt ] && echo “yes” || echo “no”
#判断文件是拥有写权限的

11.4.3 两个文件之间进行比较

【Linux学习笔记11】第十一章 Shell编程 - 图10
ln /root/student.txt /tmp/stu.txt
#创建个硬链接吧
[ /root/student.txt -ef /tmp/stu.txt ] && echo “yes” || echo “no” yes
#用test测试下,果然很有用

11.4.4 两个整数之间比较

【Linux学习笔记11】第十一章 Shell编程 - 图11
[ 23 -ge 22 ] && echo “yes” || echo “no” yes
#判断23是否大于等于22,当然是了
[ 23 -le 22 ] && echo “yes” || echo “no” no
#判断23是否小于等于22,当然不是了

11.4.5 字符串的判断

【Linux学习笔记11】第十一章 Shell编程 - 图12
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

11.4.6 多重条件判断

image.png
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条件语句

  1. if [ 条件判断式 ] ; then
  2. 程序
  3. fi
  4. #或者
  5. if [ 条件判断式 ]
  6. then
  7. 程序
  8. fi

单分支条件语句需要注意几个点

  • if语句使用fi结尾,和一般语言使用大括号结尾不同
  • [ 条件判断式 ]就是使用test命令判断,所以中括号和条件判断式之间必须有空格
  • then后面跟符合条件之后执行的程序,可以放在[]之后,用“;”分割。也可以换行写入,就不需要“;”了

例子:判断分区使用率

  1. #!/bin/basn
  2. # 统计根分区使用率
  3. # Author: shenchao (E-mail: shenchao@qq.com)
  4. rate=$(df -h | grep "/dev/sda3" | awk '{print $5}' | cut -d "%" -f 1)
  5. # 把根分区使用率作为变量值赋予变量rate
  6. if [ $rate -ge 80]
  7. then
  8. echo "Warning! /dev/sda3 is full!"
  9. fi

image.png

2.双分支if条件语句

  1. if [ 条件判断式 ]
  2. then
  3. 条件成立时,执行的程序
  4. else
  5. 条件不成立时,执行的另一个程序
  6. fi

例子1:备份mysql数据库

  1. #!/bin/bash
  2. #备份mysql数据库。
  3. # Author:shenchao(E-mail:shenchao@lampbrother.net)
  4. ntpdateasia.pool.ntp.org&>/dev/null
  5. #同步系统时间
  6. date=$(date+%y%m%d)
  7. #把当前系统时间按照“年月日”格式赋予变量
  8. datesize=$(du-sh/var/lib/mysql)
  9. #统计mysql数据库的大小,并把大小赋予size变量
  10. if [ -d/tmp/dbbak ]
  11. then
  12. echo"Date:$date!" > /tmp/dbbak/dbinfo.txt
  13. echo"Datasize$size">>/tmp/dbbak/dbinfo.txt
  14. cd/tmp/dbbak
  15. tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
  16. rm -rf /tmp/dbbak/dbinfo.txt
  17. else
  18. mkdir /tmp/dbbak
  19. echo"Date:date!">/tmp/dbbak/dbinfo.txt
  20. echo"Datasize:size">>/tmp/dbbak/dbinfo.txt
  21. cd/tmp/dbbak
  22. tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql
  23. dbinfo.txt &> /dev/null
  24. rm -rf /tmp/dbbak/dbinfo.txt fi

例子2:判断apache是否启动

  1. #!/bin/bash
  2. #Author:shenchao(E-mail:shenchao@lampbrother.net)
  3. port=$(nmap -sT 192.168.1.156 | grep tcp | grep http |awk'{print$2}')
  4. #使用nmap命令扫描服务器,并截取apache服务的状态,赋予变量port
  5. if [ "$port"=="open" ]
  6. then
  7. echo$(date)httpdisok!” >> /tmp/autostart-acc.log
  8. else
  9. /etc/rc.d/init.d/httpdstart &> /dev/null
  10. echo"$(date)restarthttpd!!" >> /tmp/autostart-err.log fi

3. 多分支if条件语句

  1. if [ 条件判断式1 ]
  2. then
  3. 当条件判断式1成立时,执行程序1
  4. elif [ 条件判断式2 ]
  5. then
  6. 当条件判断式2成立时,执行程序2
  7. „省略更多条件…
  8. else
  9. 当所有条件都不成立时,最后执行此程序
  10. fi

例子

  1. #!/bin/bash
  2. #判断用户输入的是什么文件
  3. #Author:shenchao(E-mail:shenchao@lampbrother.net)
  4. read-p"Pleaseinputafilename:"file
  5. #接收键盘的输入,并赋予变量file
  6. if [ -z "$file" ]
  7. #判断file变量是否为空
  8. then
  9. echo "Error,pleaseinputafilename"
  10. exit 1
  11. elif [ ! -e "$file" ]
  12. #判断file的值是否存在
  13. then
  14. echo "Yourinputisnotafile!"
  15. exit 2
  16. elif [ -f "$file" ]
  17. #判断file的值是否为普通文件
  18. then
  19. echo "$fileisaregularefile!"
  20. elif [ -d "$file" ]
  21. #判断file的值是否为目录文件
  22. then
  23. echo "$fileisadirectory!"
  24. else
  25. echo"$fileisanotherfile!"
  26. fi

11.5.2 case语句

多分支case条件语句

  • case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。 ```shell case $ 变量名 in
    “ 值1”)
    1. 如果变量的值等于值1,则执行程序1
    2. ; ;
    “ 值2”)
    1. 如果变量的值等于值2,则执行程序2
    2. ; ;
    3. …省略其他分支…
    4. *
    5. 如果变量的值都不是以上的值,则执行此程序
    6. ; ;
    esac
  1. ```shell
  2. #!/bin/bash
  3. #判断用户输入
  4. #Author:shenchao(E-mail:shenchao@lampbrother.net)
  5. read -p"Pleasechooseyes/no:" -t 30 cho
  6. case $cho in
  7. "yes")
  8. echo"Yourchooseisyes!"
  9. ;;
  10. "no")
  11. echo"Yourchooseisno!"
  12. ;;
  13. *)
  14. echo"Yourchooseiserror!"
  15. ;;
  16. esac

11.5.3 for循环

语法一

  1. for 变量 in 1 2 3
  2. do
  3. 程序
  4. done
  1. #!/bin/bash
  2. for city in beijing nanjing shanghai
  3. do
  4. echo "this city is $city "
  5. done

输出:
this city is beijing
this city is nanjing
this city is shanghai

  1. #!/bin/bash
  2. #批量解压缩脚本
  3. cd /sh
  4. ls *.tar.gz -> ls.log
  5. for i in $(cat ls.log)
  6. do
  7. tar -zxf $i &> /dev/null
  8. done
  9. rm -rf /sh/ls.log

语法二

  1. for((初始值;循环控制条件;变量变化))
  2. do
  3. 程序
  4. done
  1. #!/bin/bash
  2. #从1加到100
  3. #Author
  4. num=0
  5. for((i=1;i<=100;i++))
  6. do
  7. num=$(($num+$i))
  8. done
  9. echo "this sum of 1+2+...+100 is: $num"

11.5.4 while循环与until循环

1.while循环

  • while循环是不定循环,也称作条件循环。只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。这就和for的固定循环不太一样了。
    1. while [ 条件判断式 ]
    2. do
    3. 程序
    4. done
    ```shell

    !/bin/bash

    从1加到100

    Author:shenchao(E-mail:shenchao@lampbrother.net)

    i=1 s=0 while [ $i -le 100 ]

    如果变量i的值小于等于100,则执行循环

    do
    1. s=$(($s+$i))
    2. i=$(($i+1))
    done echo”Thesumis:$s”
  1. <a name="GtmvV"></a>
  2. #### 2.until循环
  3. - until循环,和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。
  4. ```shell
  5. until [ 条件判断式 ]
  6. do
  7. 程序
  8. done
  1. #!/bin/bash
  2. #从1加到100
  3. #Author:shenchao(E-mail:shenchao@lampbrother.net)
  4. i=1
  5. s=0
  6. until [ $i -gt 100 ]
  7. #循环直到变量i的值大于100,就停止循环
  8. do
  9. s=$(($s+$i))
  10. i=$(($i+1))
  11. done
  12. echo"Thesumis:$s"

11.6 函数

语法:

  1. [ function ] funname [()]{
  2. action;
  3. [return int;]
  4. }

[] -> 表示可以省略
tips:在以上的函数语法中,前面的funcation 表示声明一个函数!!! 可以不写 return -n 是指退出函数
参考:https://www.cnblogs.com/YankaiJY/p/8832436.html

  1. #!/bin/bash
  2. funWithReturn(){
  3. echo "这个函数会对输入的两个数字进行相加运算..."
  4. echo "输入第一个数字: "
  5. read aNum
  6. echo "输入第二个数字: "
  7. read anotherNum
  8. echo "两个数字分别为 $aNum 和 $anotherNum !"return $(($aNum+$anotherNum))}
  9. funWithReturn
  10. echo "输入的两个数字之和为 $? !"

输出:

  1. [timo@local1 sh]$ sh function.sh
  2. 这个函数会对输入的两个数字进行相加运算...
  3. 输入第一个数字:
  4. 12
  5. 输入第二个数字:
  6. 22
  7. 两个数字分别为 12 22 !
  8. 输入的两个数字之和为 34 !