shell编程
一、正则表达式
1. 基础正则表达式
元字符 作用 * 前一个字符匹配0次或任意多次 . 匹配除了换行符外任意一个字符 ^ 匹配行首。例:^hello会匹配以hello开头的行 $ 匹配行尾。例:hello$会匹配以hello结尾的行 [] 匹配中括号内的任意一个字符,只匹配一个字符。例:[a-z]匹配任意一个小写字母,[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位的小写字母
在/etc/profile.d/*.sh文件中建立别名
[root@centos2 ~]# vim /etc/profile.d/custom.sh alias sudo='sudo ' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias grep='grep --color=auto' alias ls='ls --color=auto' alias l.='ls -d .* --color=auto' alias ll='ls -l --color=auto' alias l='ls -laF' alias cp='cp -i' alias mv='mv -i' alias rm='rm -i' alias now='date +%Y%m%d%H%M%S'
2.1 建立练习文件
[root@centos2 ~]# vim test.txt aaaaaaaaaaa bbbbbbbbbb ccccccccc dddddd 123abc hello world faiz
faaaiiiz kabuto 555 build wwwww joker HELLO WORLD zio
2.2 "*"前一个字符匹配0次,或任意多次
[root@centos2 ~]# grep "a*" test.txt aaaaaaaaaaa bbbbbbbbbb ccccccccc dddddd 123abc hello world faiz
faaaiiiz kabuto 555 build wwwww joker HELLO WORLD zio
如果正则表达式写成"aa*"代表这行字符串一定要有一个a,但是后面有没有a都可以。也就是说会匹配至少含有一个a的行
[root@centos2 ~]# grep "aa*" test.txt aaaaaaaaaaa 123abc faiz kabuto
如果正则表达式是"aaa*",则会匹配至少包含两个连续a的字符串
[root@centos2 ~]# grep "aaa*" test.txt aaaaaaaaaaa
[root@centos2 ~]# grep "555*" test.txt 555 [root@centos2 ~]# grep "5555*" test.txt 555 [root@centos2 ~]# grep "55555*" test.txt [root@centos2 ~]#
#由于此文件中并没有4个连续的5的存在,故匹配不到
2.3 "."匹配除了换行符外任意一个字符
正则表达式"."只能匹配一个字符,这个字符可以是任意字符
[root@centos2 ~]# grep "f..z" test.txt faiz #“f..z”会匹配在f和z这两个字母之间一定有两个字符的单词 [root@centos2 ~]# grep "f.*z" test.txt faiz faaaiiiz
[root@centos2 ~]# grep ".*" test.txt aaaaaaaaaaa bbbbbbbbbb ccccccccc dddddd 123abc hello world faiz faaaiiiz kabuto 555 build wwwww joker HELLO WORLD zio
2.4 "^"匹配行首,"$"匹配行尾
"^"匹配行首,如"^f"会匹配以小写"f"开头的行
[root@centos2 ~]# grep "^f" test.txt faiz faaaiiiz
"$"匹配行尾,如"d$"会匹配以小写"d"结尾的行
[root@centos2 ~]# grep "d$" test.txt dddddd hello world build
而"^$"会匹配空白行
[root@centos2 ~]# grep -n "^$" test.txt
2.5 "[]"匹配中括号中指定的任意一个字符,只匹配一个字符
"[]"会匹配中括号中指定的任意一个字符,注意只能匹配一个字符。比如[ab]不是匹配一个a字符,就是匹配一个b字符
[root@centos2 ~]# grep "f[az]iz" test.txt faiz
"[0-9]"会匹配任意一个数字
[root@centos2 ~]# grep "[0-9]" test.txt 123abc 555
"[A-Z]"会匹配一个大写字母
[root@centos2 ~]# grep "[A-Z]" test.txt HELLO WORLD
"^[a-z]"则会匹配以小写字母开头的行
[root@centos2 ~]# grep "^[a-z]" test.txt aaaaaaaaaaa bbbbbbbbbb ccccccccc dddddd hello world faiz faaaiiiz kabuto build wwwww joker zio
2.6 "[^]"匹配中括号中的字符以外的任意一个字符
[root@centos2 ~]# grep "^[^a-z]" test.txt 123abc 555 HELLO WORLD
"^[^a-zA-Z]"会匹配不以字母开头的行
[root@centos2 ~]# grep "^[^a-zA-Z]" test.txt 123abc 555
2.7 "\"转义符
[root@centos2 ~]# grep "\.$" test.txt #匹配以"."结尾的行
2.8 "\{n\}"表示其前面的字符恰好出现n次
[root@centos2 ~]# grep "a\{3\}" test.txt aaaaaaaaaaa faaaiiiz
#第一行包含有连续的三个a,故会被匹配。
如果想要只显示三个连续的a,
[root@centos2 ~]# grep "fa\{3\}" test.txt faaaiiiz
如果正则是"[0-9]\{3\}"会匹配包含连续是三个数字的行
[root@centos2 ~]# grep "[0-9]\{3\}" test.txt 123abc 555
2.9 "\{n,\}"表示其前面的字符出现不小于n次
"\{n,\}"会匹配前面的字符出现最少n次。比如"fa\{3,\}z"这个正则就会匹配以f开头,z结尾,中间最少有三个a的字符串,"^[0-9]\{3,\}[a-z]"这个正则会匹配最少有连续三个数字开头的字符串
oot@centos2 ~]# grep "^[0-9]\{3,\}[a-z]" test.txt 123abc
2.10 "\{n,m\}"匹配其前面的字符至少出现n次,最多出现m次
[root@centos2 ~]# grep "f[a-z]\{1,3\}" test.txt faiz faaaiiiz [root@centos2 ~]# grep "f[a-z]\{1,3\}z" test.txt faiz [root@centos2 ~]# grep "f[a-z]\{2,3\}z" test.txt faiz [root@centos2 ~]# grep "f[a-z]\{2,3\}" test.txt faiz faaaiiiz [root@centos2 ~]# grep "f[a-z]\{3,6\}z" test.txt faaaiiiz
3. 扩展正则表达式
在正则表达式中还可以支持一些元字符,比如"+","?","|","()"。但是grep命令默认不支持。如果想要支持这些元字符,必须使用egrep命令或grep -E选项。故这些元字符被称为扩展元字符。
扩展元字符 作用 + 前一个字符匹配1次或任意多次 如"go+gle"会匹配"gogle"、"google"或"gooogle",当前如果"o"有更多个,也能匹配 ? 前一个字符匹配0次或1次 如"colou?r"可以匹配"colour"或"color" | 匹配两个或多个分支选择 如"was|his" 会匹配既包含"was"的行,也匹配包含"his"的行 () 匹配其整体为一个字符,即模式单元。可以理解为由多个单个字符组成的大字符 如"(dog)+"会匹配“dog”、“dogdog”、“dogdogdog”等,因为被()包含的字符会被当成一个整体,但"hello (world|earth)"会匹配到"hello world"及"hello earth"
二、字符截取和替换命令
1. cut列截取命令
[root@centos2 ~]# cut [选项] 文件名 选项 -f 列号 提取第几列 -d 分隔符 按照指定分隔符分割列 -c 字符范围 不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段提取。"n-"表示从第n个字符到行尾;"n-m"从第n个字符到第m个字符;"-m"表示从第一个字符到第m个字符
cut命令默认分隔符是制表符,就是"tab"键,但是对空格符的支持不好。
[root@centos2 ~/sh]# vim student.txt ID NAME gender Mark 1 zs M 88 2 ls M 92 3 ww M 83
[root@centos2 ~/sh]# cut -f 2 student.txt NAME zs ls ww #提取第二列内容
想要提取多列,只需列号之间使用","隔开即可
[root@centos2 ~/sh]# cut -f 2,3 student.txt NAME gender zs M ls M ww M
cut也可以按照字符提取,需要注意"8-"代表的是提取所有行的第八个字符开始到行尾,而"10-20"代表提取所有行的第十个字符到第二十个字符,而"-8"代表提取所有行从行首到第八个字符
[root@centos2 ~/sh]# cut -c 8- student.txt gender Mark 88 92 83
#提取第八个字符开始到行尾
[root@centos2 ~/sh]# cut -d ":" -f 1,3 /etc/passwd root:0 ... #以“:”作为分隔符,提取/etc/passwd文件的第一列和第三列
[root@centos2 ~/sh]# df -h | cut -d " " -f 1,3 文件系统 devtmpfs tmpfs tmpfs tmpfs /dev/mapper/centos-root /dev/sda1 tmpfs tmpfs /dev/sr0 #cut对空格符的支持不好
2. awk
2.1 printf格式化输出
[root@centos2 ~/sh]# 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键
修改student.txt文件
[root@centos2 ~/sh]# vim student.txt ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 75 85 86 82 3 ww 80 90 85 85
使用printf命令输出这个文件的内容
[root@centos2 ~/sh]# printf '%s' $(cat student.txt) IDNAMEPHPLINUXMYSQLAVERAGE1zs809070802ls758586823ww80908585[root@centos2 ~/sh]#
printf命令如果不指定输出格式,就会把所有输出内容连在一起输出。其实文本的输出本身就是这样的,cat等文本输出命令之所以可以按照格式输出,是因为cat命令已经设定了输出格式。为了printf输出合理的格式,
[root@centos2 ~/sh]# printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat student.txt) #注意printf命令的单引号中,只能识别格式输出符号,手动输入的格式是无效的 ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 75 85 86 82 3 ww 80 90 85 85
如果不把成绩当成字符串输出,而是按照整形和浮点型输出
[root@centos2 ~/sh]# printf '%i\t %s\t %i\t %i\t %i\t %8.2f\t \n' $(cat student.txt | grep -v NAME) 1 zs 80 90 70 80.00 2 ls 75 85 86 82.00 3 ww 80 90 85 85.00
2.2 awk基本使用
[root@centos2 ~/sh]# awk '条件1{动作1} 条件2{动作2}...' 文件名 条件(Pattern) 一般使用关系表达式作为条件。例 x > 10 判断变量x是否大于10 x == y 判断变量x是否等于变量y A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串 A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串 动作(Action) 格式化输出 流程控制语句
[root@centos2 ~/sh]# awk '{printf $2 "\t" $6 "\n"}' student.txt NAME AVERAGE zs 80 ls 82 ww 85 #输出第二列和第六列
截取df命令的结果
[root@centos2 ~/sh]# df -h | awk '{print $1 "\t" $3}' 文件系统 已用 devtmpfs 0 tmpfs 0 tmpfs 11M tmpfs 0 /dev/mapper/centos-root 7.5G /dev/sda1 237M tmpfs 0 tmpfs 44K /dev/sr0 11G [root@centos2 ~/sh]# du -sh student.txt | awk '{print $1 }' 4.0K [root@centos2 ~/sh]# du -sh student.txt | awk '{print $1 }' | cut -d 'K' -f 1 4.0
2.3 awk的条件
条件类型 | 条件 | 说明 |
awk保留字 | BEGIN | 在awk程序一开始时,尚未读取任何数据之前执行。BEGIN后的动作只在程序开始时执行一次 |
END | 在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次 | |
关系运算符 | > | 大于 |
< | 小于 | |
>= | 大于等于 | |
<= | 小于等于 | |
== | 等于。用于判断两个值是否相等,如果给变量赋值,只有"="号 | |
!= | 不等于 | |
A~B | 判断字符串A中是否包含能匹配B表达式的子字符串 | |
A!~B | 判断字符串A中是否不包含能匹配B表达式的子字符串 | |
正则表达式 | /正则/ | 如果在"//"中可以写入字符,也可以支持正则表达式 |
2.3.1 BEGIN
BEGIN是awk的保留字,是一种特殊的条件类型。BEGIN的执行时机是在"awk程序一开始时,尚未读取任何数据之前执行"。一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据,BEGIN的条件就不再成立,故BEGIN定义的动作只能被执行一次。例
[root@centos2 ~/sh]# awk 'BEGIN{printf "this is a transcript \n"} {printf $2 "\t" $6 "\n"}' student.txt #awk命令只要检测不到完整的单引号不会执行 #这里定义了两个动作 #第一个动作使用BEGIN条件,所有会在读入文件数据前打印"this is a transcript"(之后执行一次) #第二个动作会打印文件的第二列和第六列
this is a transcript NAME AVERAGE zs 80 ls 82 ww 85
2.3.2 END
END也是awk的保留字,只不过和BEGIN刚好相反。END是在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次。例
[root@centos2 ~/sh]# awk 'END{printf "the end \n"} {printf $2 "\t" $6 "\n"}' student.txt #在输出结尾输入“the end”,这并不是文档本身的内容,而且只会执行一次 NAME AVERAGE zs 80 ls 82 ww 85 the end
2.3.3 关系运算符
例
[root@centos2 ~/sh]# cat student.txt | grep -v NAME \ > | awk '$6 >= 85 {printf $2 "\n"}' #使用cat输出文件内容,用grep取反包含“NAME”的行 #判断第六列(平均成绩)大于等于85分的行,如果条件成立,则打印第六列 ww
加入了条件之后,只有条件成立动作才会执行,如果条件不满足,则动作不会执行。虽然awk是列提取命令,但是也要按行来读入。这个命令的执行过程如下:
1)如果有BEGIN条件,则会执行BEGIN定义的动作
2)如果没有BEGIN条件,则读入第一行,把第一行的数据一次赋予$0、$1、$2等变量。其中$0代表此行的整体数据,$1代表第一字段,$2代表第二字段
3)依据条件类型判断动作是否执行。如果条件符合,则执行动作,否则读入下一行数据。如果没有条件,则每行都会执行动作
4)读入下一行数据,重复执行一行步骤
例,查看zs用户的平均成绩
[root@centos2 ~/sh]# awk '$2 ~ /zs/ {printf $6 "\n"}' student.txt #如果第二字段中输入包含有"zs"字符,则打印第六列数据 80
这里要注意在awk中,使用"//"包含的字符串,awk命令才会查找。也就是说字符串必须用"//"包含,awk才能正确识别。
2.3.4 正则表达式
如果想让awk识别字符串,必须使用"//"包含。例
[root@centos2 ~/sh]# awk '/ls/ {print}' student.txt #打印ls的成绩 2 ls 75 85 86 82
当使用df命令查看分区使用情况时,如果我只想查看真正的系统分区的使用状况,而不想看光盘和临时分区的使用状况,可以
[root@centos2 ~/sh]# df -h |awk '/sda[0-9]/ {print $1 "\t" $5 "\n"}' #查询包含sda数字的行,并打印第一第五列 /dev/sda1 24%
2.4 awk内置变量
awk内置变量 | 作用 |
$0 | 代表目前awk所读入的整行数据。已知awk是一行一行读入数据的,$0就代表当前读入行的整行数据 |
$n | 代表目前读入行的第n个字段 |
NF | 当前行拥有的字段(列)总数 |
NR | 当前awk所处理的行,是总数据的第几行 |
FS | 用户定义分隔符。awk默认的分隔符是任何空格,如果想要使用其他分隔符(如":"),需要FS变量定义 |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
FNR | 当前文件中的当前记录数(对输入文件起始为1) |
OFMT | 数值的输出格式(默认为%.6g) |
OFS | 输出字段的分隔符(默认为空格) |
ORS | 输出记录分割符(默认为换行符) |
RS | 输入记录分隔符(默认为换行符) |
[root@centos2 ~]# cat /etc/passwd |grep "/bin/bash" | \ > awk '{FS=":"} {printf $1 "\t" $3 "\n"}' #查询可以登录的用户的用户名和UID root:x:0:0:root:/root:/bin/bash shw 1000 sss 1001
这里":"分隔符生效了,但是在第一行却没起作用,需加上"BEGIN"条件
[root@centos2 ~]# cat /etc/passwd |grep "/bin/bash" | \ > awk 'BEGIN{FS=":"} {printf $1 "\t" $3 "\n"}' root 0 shw 1000 sss 1001
[root@centos2 ~]# cat /etc/passwd | grep "/bin/bash" | \ > awk 'BEGIN{FS=":"} {printf $1 "\t" $3 "\t 行号: " NR "\t 列数: " NF "\n"}' #开始执行{分隔符是“:”} {输出第一字段和第三字段 输出行号(NR值) 列数(NF值)} root 0 行号: 1 列数: 7 shw 1000 行号: 2 列数: 7 sss 1001 行号: 3 列数: 7
只想看sshd这个伪用户的相关信息,可以这样
[root@centos2 ~]# cat /etc/passwd | \ > awk 'BEGIN{FS=":"} $1=="sshd" {printf $1 "\t" $3 "\t 行号: " NR "\t 列数:" NF "\n"}' #可以看到sshd伪用户的UID是74,是/etc/passwd文件的第40行,此行有7列(7个字段) sshd 74 行号: 40 列数:7
2.5 awk流程控制
首先看一下student.txt这个文件的内容
[root@centos2 ~/sh]# cat student.txt ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 75 85 86 82 3 ww 80 90 85 85
先看看如何在awk中定义变量与调用变量的值。假如我想统计LINUX成绩的总分,
[root@centos2 ~/sh]# awk 'NR==2{LINUX1=$4} NR==3{LINUX2=$4} NR==4{LINUX3=$4;totle=LINUX1+LINUX2+LINUX3;print "totle linux is " totle}' student.txt #统计LINUX成绩的总分 totle linux is 265
解释一下这个命令。"NR==2{LINUX1=$4}"(条件是NR==2,动作是LINUX=$4)这句话指如果输入数据是第二行(第一行为标题行),就把第二行的第四列的值赋予变量"LINUX1"。"NR==2{LINUX1=$4}"这句话指如果输入数据是第三行,就把第三行的第四列的值赋予变量"LINUX1"。"NR==4{LINUX=$4;totle=LINUX1+LINUX2+LINUX3;print "totle linux is " totle}"("NR==4"是条件,后面{}中的都是动作)这句话指如果输入数据是第四行,就把第四行的第四列的值赋予变量"LINUX3";然后定义变量totle的值为"LINUX1+LINUX2+LINUX3";然后输出"totle linux is"关键字,然后加变量totle的值。
在awk中,因为命令语句非常长,在输入格式时需要注意以下内容:
多个条件{动作}可以用空格分割,也可以用回车分割
在一个动作中,如果需要执行多个命令,需要用";"分割,或者回车分割
在awk中,变量的赋值与调用都不需要加入"$"符
条件中判断两个值是否相同,使用"==",以便和变量赋值进行区分
再看一下如何实现流程控制,
[root@centos2 ~/sh]# awk '{if (NR>=2) {if ($4>=90) printf $2 " is better\n"}}' student.txt #程序中有两个if判断,第一个判断行号大于2,第二个判断linux成绩大于等于90分 zs is better ww is better
其实在awk中if判断语句,完全可以直接利用awk自带的条件来取代
[root@centos2 ~/sh]# awk 'NR>=2 {test=$4} test>=90 {printf $2 " is better\n"}' student.txt #先判断行号如果大于2,就把第四个字段赋予变量test #再判断如果test的值大于等于90,就打印is better zs is better ww is better
2.6 awk函数
awk编程也允许在编程时使用函数。awk函数的自定义方法:
function 函数名 (参数列表) { 函数体 }
定义一个简单的函数,使用函数打印出student.txt文件的学员姓名和平均成绩
[root@centos2 ~/sh]# awk 'function test(a,b) {printf a "\t" b "\n"} { test($2,$6) }' student.txt #定义函数test,包含两个参数,函数体的内容是输出这两个参数的值 #调用函数test,并向两个参数传递值 NAME AVERAGE zs 80 ls 82 ww 85
2.7 awk中调用脚本
对于小的单行程序来说,将脚本作为命令行自变量传递给awk是非常简单的,而对于多行程序就比较难处理。当程序时多行的时候,使用外部脚本是很合适的。首先在外部文件中写好脚本,然后使用awk的-f选项,使其读入脚本并且执行。
例,先编写一个awk脚本
[root@centos2 ~/sh]# vim pass.awk BEGIN {FS=":"} { print $1 "\t" $3}
然后使用"-f"选项调用这个脚本
[root@centos2 ~/sh]# awk -f pass.awk /etc/passwd root 0 bin 1 daemon 2 ...
3. sed命令
sed主要是用来将数据进行选取、替换、删除、新增的命令
[root@centos2 ~/sh]# sed [选项] '[动作]' 文件名 选项 -n 一般sed命令会把所有的数据都输出到屏幕,如果加入此选项,则只会把经过sed命令处理的行输出到屏幕 -e 允许对输入数据应用多条sed命令编辑 -f 脚本文件名 从sed脚本中读入sed操作。和awk的-f选项类似 -r 在sed中支持扩展正则表达式 -i 用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出 动作 a \ 追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要用“\”代表数据未完结 c \ 行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用"\"代表数据未完结 i \ 插入,在当前行前插入一行或多行。插入多行时,除最后一行外,每行末尾需用"\"代表数据未完结 d 删除,删除指定的行 p 打印,输出指定的行 s 字串替换,用一个字符串替换另外一个字符串。格式为"行范围s/旧字串/新字串/g"(和vim中的替换类似)
对sed命令需注意,sed所做的修改并不会直接改变文件的内容(如果是用管道符接受的命令的输入,这种情况连文件都没有),而是把修改结果只显示到屏幕上,除非使用"-i"选项才会直接修改文件。
3.1 行数据操作
查看student.txt文件中的第二行,可使用"p"动作
[root@centos2 ~/sh]# sed '2p' student.txt ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 1 zs 80 90 70 80 2 ls 75 85 86 82 3 ww 80 90 85 85
"p"命令确实输出了第二行数据,但是sed命令还会把所有数据都输出一次,这是就会看到上述这个奇怪的结果。如果想要指定输出某行数据,需使用"-n"选项
[root@centos2 ~/sh]# sed -n '2p' student.txt 1 zs 80 90 70 80
如何删除文件的数据
[root@centos2 ~/sh]# sed '2,4d' student.txt #删除第二行到第四行的数据 ID NAME PHP LINUX MYSQL AVERAGE [root@centos2 ~/sh]# cat student.txt #但是文件本身并没有修改 ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 75 85 86 82 3 ww 80 90 85 85
如何追加和插入行数据
[root@centos2 ~/sh]# sed '2a hello' student.txt #在第二行后加入hello ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 hello 2 ls 75 85 86 82 3 ww 80 90 85 85
"a"会在指定行后面追加入数据,如果想要在指定行前面插入数据,则需要使用动作"i"
[root@centos2 ~/sh]# sed '2i hello world' student.txt #在第二行前面插入数据 ID NAME PHP LINUX MYSQL AVERAGE hello world 1 zs 80 90 70 80 2 ls 75 85 86 82 3 ww 80 90 85 85
如果想要追加或插入多行数据,除最后一行外,每行的末尾都要加入"\"代表数据未完结。再来看看"-n"选项的作用
[root@centos2 ~/sh]# sed -n '2i hello \ > world' student.txt hello world
"-n"只查看sed命令操作的数据,而不是所有的数据。
如何实现行数据的替换。
[root@centos2 ~/sh]# cat student.txt | sed '2c no such person' ID NAME PHP LINUX MYSQL AVERAGE no such person 2 ls 75 85 86 82 3 ww 80 90 85 85
sed命令默认情况是不会修改文件内容的,如果确定要让sed命令直接处理文件的内容,可以使用"-i"选项。该选项需谨慎使用。
[root@centos2 ~/sh]# sed -i '2c No such person' student.txt [root@centos2 ~/sh]# cat student.txt ID NAME PHP LINUX MYSQL AVERAGE No such person 2 ls 75 85 86 82 3 ww 80 90 85 85
3.2 字符串替换
"c"动作是进行行替换的,仅仅想要替换行中的部分数据,需使用"s"动作,格式如下:
[root@centos2 ~/sh]# sed -i 's/旧字串/新字串/g' 文件名
替换的格式和vim非常类似
[root@centos2 ~/sh]# cat students.txt ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 92 85 86 82 3 ww 80 90 85 85 [root@centos2 ~/sh]# sed -i '4s/90/92/g' students.txt #在第四行中,把90替换成92 [root@centos2 ~/sh]# cat students.txt ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 92 85 86 82 3 ww 80 92 85 85
想把某行数据注释掉
[root@centos2 ~/sh]# sed -i '4s/^/#/g' students.txt #使用正则表达式,"^"代表行首 [root@centos2 ~/sh]# cat students.txt ID NAME PHP LINUX MYSQL AVERAGE 1 zs 80 90 70 80 2 ls 92 85 86 82 #3 ww 80 92 85 85
在sed中只能指定行范围
[root@centos2 ~/sh]# sed -e 's/zs//g ; s/ww//g' students.txt #同时把“zs”和“ww”替换为空 ID NAME PHP LINUX MYSQL AVERAGE 1 80 90 70 80 2 ls 92 85 86 82 #3 80 92 85 85
"-e"选项可以同时执行多个sed动作,当然如果只是执行一个动作也可以使用"-e"选项,但是没什么意义。注意,多个动作之间要用";"号或回车分割。
[root@centos2 ~/sh]# sed -e 's/zs//g > s/ww//g' students.txt ID NAME PHP LINUX MYSQL AVERAGE 1 80 90 70 80 2 ls 92 85 86 82 #3 80 92 85 85
三、字符处理命令
1. 排序命令sort
[root@centos2 ~/sh]# sort [选项] 文件名 选项 -f 忽略小写 -b 忽略每行前面的空白部分 -n 以数值型进行排序,默认使用字符串型排序 -r 反向排序 -u 删除重复行。就是uniq命令 -t 指定分隔符,默认分隔符是制表符 -k n[,m] 按照指定的字段范围排序、从第n字段开始,m字段结束(默认到行尾)
sort命令默认是用每行开头第一个字符来进行排序的,例
[root@centos2 ~/sh]# sort /etc/passwd #排序用户信息文件 abrt:x:173:173::/etc/abrt:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin chrony:x:992:989::/var/lib/chrony:/sbin/nologin colord:x:996:993:User for colord:/var/lib/colord:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
如果想反向排序,使用"-r"选项
[root@centos2 ~/sh]# sort -r /etc/passwd #反向排序 usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin unbound:x:991:988:Unbound DNS resolver:/etc/unbound:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync sss:x:1001:1002::/home/sss:/bin/bash ...
想要指定排序的字段,需使用"-t"选项指定分隔符,并使用"-k"选项指定字段号,假如想要按照UID字段排序/etc/passwd文件
[root@centos2 ~/sh]# sort -t ":" -k 3,3 /etc/passwd #指定分隔符是“:”,用第三段开头,第三段结尾排序,就是只有第三字段排序 root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin shw:x:1000:1000:shw:/home/shw:/bin/bash sss:x:1001:1002::/home/sss:/bin/bash qemu:x:107:107:qemu user:/:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin pulse:x:171:171:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin ...
仔细看发现daemon用户的UID是2,却排在下面,这是因为sort默认是按照字符排序,前面用户的UID的第一个字符都是1,所有这么排序。要想按照数字排序,请使用"-n"选项
[root@centos2 ~/sh]# sort -n -t ":" -k 3,3 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin ...
当然"-k"选项可以直接使用"-k 3",代表从第三字段到行尾都排序(第一个字符先排序,如果一致,第二个字符再排序,直到行尾)
2. uniq
uniq命令是用来取消重复行的命令,其实和"sort -u"选项都是一样的。格式如下:
[root@centos2 ~/sh]# uniq [选项] 文件名 选项 -i 忽略大小写
3. wc统计命令
[root@centos2 ~/sh]# wc [选项] 文件名 选项 -l 只统计行数 -w 只统计单词数 -m 只统计字符数
四、条件判断
1. 按照文件类型进行判断
测试选项 | 作用 |
-b 文件 | 判断该文件是否存在,并且是否为块设备文件(是块设备文件为真) |
-c 文件 | 判断该文件是否存在,并且是否为字符设备文件(是字符设备文件为真) |
-d 文件 | 判断该文件是否存在,并且是否为目录文件(是目录为真) |
-e 文件 | 判断该文件是否存在(存在为真) |
-f 文件 | 判断该文件是否存在,并且是否为普通文件(是普通文件为真) |
-L 文件 | 判断该文件是否存在,并且是否为符号链接文件(是符号链接文件为真) |
-p 文件 | 判断该文件是否存在,并且是否为管道文件(是管道文件为真) |
-s 文件 | 判断该文件是否存在,并且是否为非空(非空为真) |
-S 文件 | 判断该文件是否存在,并且是否为套接字文件(是套接字文件为真) |
[root@centos2 ~/sh]# [ -e /root/sh ] [root@centos2 ~/sh]# echo $? 0 #判断结果为0,/root/sh目录存在 [root@centos2 ~/sh]# [ -e /root/test ] [root@centos2 ~/sh]# echo $? 1 #在/root/下并没有test文件或目录,故"$?"的返回值非零
多命令顺序执行"&&"和"||"。可以再判断一下/root/sh是否为目录
[root@centos2 ~/sh]# [ -d /root/sh ] && echo "yes" || echo "no" yes #第一个判断命令如果正确执行,则打印"yes",否则打印"no"
2. 按照文件权限进行判断
test是非常完善的判断命令,还可以判断文件的权限
测试选项 | 作用 |
-r 文件 | 判断该文件是否存在,并且是否该文件拥有读权限(有读权限为真) |
-w 文件 | 判断该文件是否存在,并且该文件是否拥有写权限(有写权限为真) |
-x 文件 | 判断该文件是否存在,并且该文件是否拥有执行权限(有执行权限为真) |
-u 文件 | 判断该文件是否存在,并且该文件是否拥有SUID权限(有SUID权限为真) |
-g 文件 | 判断该文件是否存在,并且该文件是否拥有SGID权限(有SGID权限为真) |
-k 文件 | 判断该文件是否存在,并且该文件是否拥有SBIT权限(有SBIT权限为真) |
例
[root@centos2 ~/sh]# ll 总用量 12 -rw-r--r-- 1 root root 35 12月 2 21:44 pass.awk -rw-r--r-- 1 root root 84 12月 2 22:17 students.txt -rw-r--r-- 1 root root 81 12月 2 22:11 student.txt [root@centos2 ~/sh]# [ -w student.txt ] && echo "yes" || echo "no" yes #判断该文件是否拥有写权限
3. 两个文件之间进行比较
测试选项 | 作用 |
文件1 -nt 文件2 | 判断文件1的修改时间是否比文件2的新(如果新则为真) |
文件1 -ot 文件2 | 判断文件1的修改时间是否比文件2的旧(如果旧则为真) |
文件1 -ef 文件2 | 判断文件1是否和文件2的Inode号一致,可以理解为两个文件是否为同一个文件。这个判断用于硬链接判断是很好的方法 |
如何判断两个文件是否为硬链接
[root@centos2 ~/sh]# ln /root/sh/students.txt /tmp/stus.txt #创建硬链接 [root@centos2 ~/sh]# [ /root/sh/students.txt -ef /tmp/stus.txt ] && echo "yes" || echo "no" yes
4. 两个证书之间比较
测试选项 | 作用 |
整数1 -eq 整数2 | 判断整数1是否和整数2相等(相等为真) |
整数1 -ne 整数2 | 判断整数1是否和整数2不相等(不相等为真) |
整数1 -gt 整数2 | 判断整数1是否大于整数2(大于为真) |
整数1 -lt 整数2 | 判断整数1是否小于整数2(小于为真) |
整数1 -ge 整数2 | 判断整数1是否大于等于整数2(大于等于为真) |
整数1 -le 整数2 | 判断整数1是否小于等于整数2(小于等于为真) |
例
[root@centos2 ~/sh]# [ 23 -gt 22 ] && echo "yes" || echo "no" yes #判断23是否大于22 [root@centos2 ~/sh]# [ 23 -le 22 ] && echo "yes" || echo "no" no #判断23是否小于等于22
5. 字符串的判断
测试选项 | 作用 |
-z 字符串 | 判断字符串是否为空(为空返回真) |
-n 字符串 | 判断字符串是否为非空(非空返回证) |
字串1 == 字串2 | 判断字符串1是否和字符串2相等(相等返回真) |
字串1 != 字串2 | 判断字符串1是否和字符串2不相等(不相等返回真) |
例
[root@centos2 ~/sh]# name=zs #为变量赋值 [root@centos2 ~/sh]# [ -z $name ] && echo "yes" || echo "no" no #判断name变量是否为空,因为不为空,故返回no
如何判断两个字符串相等
[root@centos2 ~/sh]# aa=11 [root@centos2 ~/sh]# bb=22 #给变量aa、bb赋值 [root@centos2 ~/sh]# [ "$aa" == "$bb" ] && echo "yes" || echo "no" no #判断两个变量的值是否相等
6. 多重条件判断
测试选项 | 作用 |
判断1 -a 判断2 | 逻辑与,判断1和判断2都成立,最终的结果才为真 |
判断1 -o 判断2 | 逻辑或,判断1和判断2有一个成立,最终的结果为真 |
! 判断 | 逻辑非,使原始的判断式取反 |
例
[root@centos2 ~/sh]# a=11 #赋值 [root@centos2 ~/sh]# [ -n "$a" -a "$a" -gt 22 ] && echo "yes" || echo "no" no #判断变量a是否有值,同时判断变量a是否大于22 #因为变量a的值不大于22,所以虽然第一个判断值为真,返回的结果仍是假
[root@centos2 ~/sh]# a=23 [root@centos2 ~/sh]# [ -n "$a" -a "$a" -gt 22 ] && echo "yes" || echo "no" yes
逻辑非
[root@centos2 ~/sh]# [ ! -n "$a" ] && echo "yes" || echo "no" no #变量a的值非空,返回值为真 #加!后,判断值会取反,故当a有值时,返回值是假 [root@centos2 ~/sh]# [ ! -z "$a" ] && echo "yes" || echo "no" yes
五、流程控制
1. if条件判断
1.1 单分支if条件语句
单分支条件语句最简单,就是只有一个判断条件,如果符合条件则执行某个程序,否则什么事情都不做。语法如下:
if [ 条件判断式 ];then 程序 fi
单分支条件语句需要注意几个点:
if语句使用fi结尾,和一般语言使用大括号结尾不同
[ 条件判断式 ]就是使用命令判断,中括号和条件判断式之间必须有空格
then后面跟符合条件之后执行的程序,可以放在[]之后,用";"分割。也可以换行写入,就不需要";"了,如:
if [ 条件判断式 ] then 程序 fi
[root@centos2 ~/sh]# vi if1.sh #!/bin/bash #author by nanshan 20191202 rate=$(df -h | grep /dev/mapper/centos-root |awk '{print $5}' | cut -d "%" -f 1) #把根分区使用率最为变量值赋予变量rate if [ $rate -ge 80 ];then #判断rate的值,如果大于等于80,则执行以下程序 echo "Warning! /dev/sda3 is full!" #打印警告信息。也可以向管理员发送邮件 fi
1.2 双分支if条件语句
if [ 条件判断式 ];then 条件成立时,执行程序1 else 条件不成立时,执行程序2 fi
例,数据备份
[root@centos2 ~/sh]# vim if2.sh #!/bin/bash #author by nanshan 20191202 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" >> /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
在工作中,服务器经常会宕机。通过脚本监听本机的服务,如果服务停止或宕机了,可以自动重启这些服务。以apache为例
[root@centos2 ~/sh]# vim autostart.sh #!/bin/bash #author by nanshan 20191203 PORT=$(nmap -sT 192.168.80.128 | 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/init.d/httpd start &>/dev/null echo "$(date) restart httpd !!" >> /tmp/autostart-acc.log fi
nmap端口扫描命令,格式如下
[root@centos2 ~/sh]# nmap -sT 域名或IP 选项 -s 扫描 -T 扫描所有开启的TCP端口
1.3 多分支if条件语句
if [ 条件判断式1 ];then 条件判断式1成立时,执行程序1 elif [ 条件判断式2 ];then 条件判断式2成立时,执行程序2 ... else 所有条件都不成立时,最后执行此程序 fi
例,使用if多分支条件句判断用户输入的是一个文件还是目录
[root@centos2 ~/sh]# vim if-elif.sh #!/bin/bash #by author nanshan 20191203 read -p "please input a filename: " file #接受键盘的输入,并赋予变量file if [ -z "$file" ];then #判断变量file是否为空 echo "ERROR,please input a filename" #如果为空,执行程序1,输出报错信息 exit 1 #退出程序,并返回值为1(返回值赋予变量$?) elif [ ! -e "$file" ];then #判断文件是否存在 echo "$your input is not a file!" #如果不存在,执行程序2 exit 2 #退出程序,并定义返回值为2 elif [ -f "$file" ];then #判断file变量的值是否为普通文件 echo "$file is a regulare file." #如果是,则执行程序3 elif [ -d "$file" ];then #判断变量file的值是否为目录 echo "$file is a directory." #如果是,则执行程序4 else echo "$file is an other file!" #如果以上判断都不是,执行最后程序 fi
2. 多分支case条件语句
case语句和if···elif···else语句一样是多分支条件语句,不过和if多分支条件语句不同的是,case只能判断一种条件关系,而if语句可以判断多种条件语句。case语句语法如下:
case $变量名 in "值1") 如果变量的值等于值1,则执行程序1 ;; "值2") 如果变量的值等于值2,则执行程序2 ;; ···略··· *) 如果变量的值不等于以上所有值,则执行此程序 ;; esac
这个语句需注意下述内容
case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不等,则依次比较下一个值。如果所有的值都不符合,则执行"*)" ("*"代表所有其他值)中的程序
case语句以"case"开头,以"esac"结尾。
每个分支程序之后要通过";;"双分号结尾,代表该程序段结束(不可忘记)。
例:判断是yes或no;
[root@centos2 ~/sh]# vim case.sh #!/bin/bash #by author nanshan 20191203 read -p "please choose yes or 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
3. for 循环
for循环是固定循环,也就是循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。for循环有两种语法:
语法一: for 变量 in 值1 值2 值3 ··· do 程序 done
这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个就循环几次,并且每次循环都把值赋予变量。
语法二: for (( 初始值;循环控制条件;变量变化)) do 程序 done
语法二中需要注意:
初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;
循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;
变量变化:每次循环之后,变量要如何变化,如 i=i+1。代表每次循环之后,变量i的值都加1。
3.1 语法一举例
例1,打印时间
[root@centos2 ~/sh]# vim for.sh #!/bin/bash #by author nanshan 20191203 for time in morning noon afternoon eveing do echo "this time is $time!" done
例2,批量解压缩
[root@centos2 ~/sh]# vim auto-tar.sh #!/bin/bash #by author nanshan 20191203 cd /lamp #进入到压缩包目录 ls *.tar.gz > ls.log #把所有以.tar.gz结尾的文件覆盖到ls.log临时文件中 for i in $(cat ls.log) #读取ls.log文件中的内容,文件有多少个值,就会循环多少次,每次循环把文件名赋予变量i do tar -zxf $i &>/dev/null #解压,并把所有的输出丢弃 done rm -rf /lamp/ls.log #删除临时文件ls.log
3.2 语法二举例
语法二和其他语言中的for循环类似,事先决定循环次数的固定循环。
例1. 从1加到100
[root@centos2 ~/sh]# vim for2.sh #!/bin/bash #by author nanshan 20191203 s=0 for (( i=1;i<=100;i++ )) do s=$(( $s+$i )) done echo "the sum of 1+2+···+100 is : $s"
例2. 批量添加用户
[root@centos2 ~/sh]# vim useradd.sh #!/bin/bash #by author nanshan 20191203 read -p "please input user name: " -t 30 name #让用户输入用户名,把输入保存到变量name read -p "please input the number of users: " -t 30 num #让用户输入添加用户的数量,把输入保存到变量num read -p "please input the password of users: " -t 30 pass #让用户输入初始密码,把输入保存到变量pass if [ ! -z "$name" -a ! -z "$num" -a ! -z "$pass" ];then #判断三个变量是否为空 y=$(echo $num | sed 's/[0-9]//g') #定义变量的值为后续命令的结果 #把变量num的值替换为空。如果能替换为空,则证明num的值为数字,反之,为非全数字 if [ -z "$y" ];then #如果变量y的值为空 for (( i=1;i<=$num;i=i+1 )) #循环num变量指定的次数 do /usr/sbin/useradd $name$i &>/dev/null #添加用户,用户名为变量name的值加上变量i的数字 echo $pass | /usr/sbin/passwd --stdin $name$i &>/dev/null #给用户设定初始密码位变量pass的值 done fi fi
例3. 批量删除用户
[root@centos2 ~/sh]# vim userdel.sh #!/bin/bash #by author nanshan 20191203 user=$(cat /etc/passwd | grep "/bin/bash" | grep -v "root" | cut -d ":" -f 1) #读取用户信息文件,提取可以登录的用户信息,取消root用户,截取第一列用户名 for i in $user
#循环,有多少个普通用户,循环几次 do userdel -r $i done
4. while循环
while [ 条件判断式 ] do 程序 done
对while循环来讲,只有条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。
例
[root@centos2 ~/sh]# vim while.sh #!/bin/bash #by author nanshan 20191203 i=1 s=0 while [ $i -le 100 ] #如果变量i的值小于等于100,就执行循环 do s=$(( $s+$i )) i=$(( $i+1 )) done echo "the sum is: $s"
5. until循环
until循环与while循环相反,until循环只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,就终止循环。
until [ 条件判断式 ] do 程序 done
例
[root@centos2 ~/sh]# vim until.sh #!/bin/bash #by author nanshan 20191203 s=0 i=1 until [ $i -gt 100 ] #循环直到变量i的值大于100,才停止循环 do s=$(( $s+$i )) i=$(( $i+1 )) done echo "the sum is: $s"
6. 函数
function 函数名 () { 程序 }
例
[root@centos2 ~/sh]# vim function.sh #!/bin/bash function sum () { #定义函数 s=0 for (( i=0;i<=$1;i=i+1 )) #循环直到i大于$1为止,$1是函数sum的第一个参数 #在函数中也可以使用位置参数变量,只是这里的$1指的是函数的第一个参数 do s=$(( $s+$i )) done echo "the sum of 1+2+3+···+$1 is: $s" #输出1加到$1的和 } read -p "please input a number: " -t 30 num #接受用户输入的数字,并把值赋予变量num y=$(echo $num | sed 's/[0-9]//g') #把变量num的值替换为空,并赋予变量y if [ -z "$y" ];then #判断y变量是否为空,以确定变量num是否为纯数字 sum $num #调用sum函数,并把变量num的值作为第一个参数传递给sum函数 else echo "ERROR! Please input a number!" #如果变量num的值不是纯数字,就输出报错信息 fi
7. 特殊流程控制语句
7.1 exit语句
系统使用exit命令的,用户退出当前用户的登录状态。在shell脚本中,exit语句是用来退出当前脚本的。就是说,在shell脚本中,只要碰到exit语句,后续的程序就不再执行,而直接退出脚本。语法如下:
exit [返回值]
如果exit命令之后定义了返回值,那么脚本执行之后的返回值就是我们自己定义的返回值。可以通过"echo $?"这个命令查看返回值。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit语句之前,最后执行的一条命令的返回值。
例
[root@centos2 ~/sh]# vim exit.sh #!/bin/bash read -p "please input a number: " -t 30 num y=$(echo $num | sed 's/[0-9]//g') [ -n "$y" ] && echo "error! please input a number!" && exit 10 #判断变量y的值,如果为空,输出报错信息,退出脚本,返回值为10 echo "the number is: $num" #如果没有退出脚本,打印变量num中的数字
7.2 break语句
当程序执行到break语句时,会结束整个当前循环。而continue语句时结束本次循环。
break结束整个循环,continue结束此次循环,下次继续
[root@centos2 ~/sh]# vim break.sh #!/bin/bash for (( i=1;i<=10;i=i+1 )) do if [ "$i" -eq 4 ];then #如果变量i的值等于4 break #退出整个循环 fi echo $i #输出变量i的值 done
7.3 continue语句
continue语句也是结束流程控制语句。在循环中,continue语句只会结束单次当前循环。
[root@centos2 ~/sh]# vim continue.sh #!/bin/bash for (( i=1;i<=10;i=i+1 )) do if [ "$i" -eq 4 ];then continue #换成continue fi echo $i done
continue只会退出当前此次循环,不影响后续的循环。