SHELL脚本基础
shell首先它是一个脚本,并不能作为正式的编程语言。因为是跑在linux的shell中,所以叫shell脚本。说白了,shell脚本就是一些命令的集合。举个例子,我想要实现这样的操作:
1)进入到/tmp/目录;
2)列出当前目录中所有的文件名;
3)把所有当前的文件拷贝到/root/目录下;
4)删除当前目录下的所有文件;
简单的4步在shell窗口中需要你敲4次命令,按4次回车。这样是不是很麻烦?当然这4步操作非常简单,如果是更加复杂的命令设置需要几十次操作呢?那样的话一次一次敲键盘会很麻烦。所以不妨把所有的操作都记录到一个文档中,然后去调用文档中的命令,这样一步操作就可以完成。其实这个文档就是shell脚本,只是这个shell脚本有它特殊的格式。
shell脚本能帮助我们很方便的去管理服务器,因为我们可以指定一个任务计划定时去执行某一个shell脚本实现我们想要需求。这对于linux系统管理员来说是一件非常值得自豪的事情。
有一个问题需要约定一下,凡是自定义的脚本建议放到/usr/local/sbin/目录下,这样做的目的是,一来可以更好的管理文档;二来以后接管你的管理员都知道自定义脚本在哪里,方便维护。
shell脚本的基本结构以及如何执行
|
1
|
[root@localhost ~]# vi test.sh |
|
1
2
3
|
#!/bin/bash<br># This is my first shell script.dateecho "Hello World!" |
shell脚本通常都是以.sh为后缀名的,这个并不是说不带.sh这个脚本就不能执行,只是大家的一个习惯而已。所以,以后你发现了.sh为后缀的文件那么它一定会是一个shell脚本了。test.sh中第一行“#!/bin/bash”它代表的意思是,该文件使用的是bash语法。如果不设置该行,那么你的shell脚本就不能执行。“#”表示注释,后面跟一些脚本相关的注释内容。当然这些注释并非必须的,如果你懒得很,可以省略,但是笔者不建议省略。因为随着你工作时间的增加,你写的shell脚本会越来越多,如果有一天你回头查看你写的某个脚本时,很可能忘记该脚本时用来干什么的以及什么时候写的。所以写上注释是有必要的。
|
1
2
3
4
|
[root@localhost ~]# sh test.shWed Sep 5 23:33:17 CST 2018Hello World![root@localhost ~]# |
shell脚本的执行很简单,直接“sh filename”即可,另外你还可以这样执行。
|
1
2
3
4
5
|
[root@localhost ~]# chmod +x test.sh[root@localhost ~]# ./test.shWed Sep 5 23:34:55 CST 2018Hello World![root@localhost ~]# |
默认我们用vim编辑的文档是不带有执行权限的,所以需要加一个执行权限,那样就可以直接使用“./filename”执行这个脚本了。另外使用sh命令去执行一个shell脚本的时候是可以加-x选项来查看这个脚本执行过程的,这样有利于我们调试这个脚本哪里出问题了。
|
1
2
3
4
5
6
|
[root@localhost ~]# sh -x test.sh+ dateWed Sep 5 23:38:44 CST 2018+ echo 'Hello World!'Hello World![root@localhost ~]# |
该shell脚本中用到了“date”这个命令,它的作用就是来打印当前系统的时间。其实在shell脚本中date使用率非常高。有几个选项笔者常常在shell脚本中用到:
|
1
2
3
|
[root@localhost ~]# date "+%Y %m %d %H:%M:%S"2018 09 06 00:20:31[root@localhost ~]# |
%Y表示年,%m表示月,%d表示日期,%H表示小时,%M表示分钟,%S表示秒
|
1
2
3
|
[root@localhost ~]# date "+%y %m %d"18 09 06[root@localhost ~]# |
注意%Y和%y的区别。
|
1
2
3
4
5
6
7
8
9
|
[root@localhost ~]# date -d "-1 day" "+%Y %m %d"2018 09 05[root@localhost ~]# date -d "+1 day" "+%Y %m %d"2018 09 07[root@localhost ~]# date -d "-1 month" "+%Y %m %d"2018 08 06[root@localhost ~]# date -d "-1 year" "+%Y %m %d"2017 09 06[root@localhost ~]# |
-d选项也是经常要用到的,它可以打印n天前或者n天后的日期,当然也可以打印n个月/年前或者后的日期。
shell脚本中的变量
|
1
|
[root@localhost ~]# vi test2.sh |
|
1
2
3
4
5
6
7
8
|
#!/bin/bashd=`date +%H:%M:%S`echo "the script begin at $d"echo "now we will sleep 2 seconds."sleep 2d1=`date +%H:%M:%S`echo "the script end at $d1" |
在test2.sh中使用到了反引号,你是否还记得它的作用?“d”和“d1”在脚本中作为变量出现,定义变量的格式为“变量名=变量值”。当在脚本中引用变量时需要加上“$”符号。下面看看脚本执行的结果吧。
|
1
2
3
4
5
|
[root@localhost ~]# sh test2.shthe script begin at 00:43:40now we will sleep 2 seconds.the script end at 00:43:42[root@localhost ~]# |
下面我们用shell计算两个数的和。
|
1
|
[root@localhost ~]# vi test3.sh |
|
1
2
3
4
5
6
|
#!/bin/basha=1b=2sum=$[$a+$b]echo "sum is $sum" |
数学计算药用“[]”括起来并且外面要带一个“$”。脚本结果为:
|
1
2
3
|
[root@localhost ~]# sh test3.shsum is 3[root@localhost ~]# |
shell脚本还可以和用户交互。
|
1
|
[root@localhost ~]# vi test4.sh |
|
1
2
3
4
5
6
7
8
|
#!/bin/bashecho "Please input a number:"read xecho "Please input another number:"read ysum=$[$x+$y]echo "The sum of two numbers is:$sum" |
这就用到了read命令了,它可以从标准输入获得变量的值,后跟变量名。“read x”表示x变量的值需要用户通过键盘输入的到。脚本执行过程如下:
|
1
2
3
4
5
6
7
|
[root@localhost ~]# sh test4.shPlease input a number:5Please input another number:6The sum of two numbers is:11[root@localhost ~]# |
我们不妨加上-x选项再来看一下这个执行过程:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[root@localhost ~]# sh -x test4.sh+ echo 'Please input a number:'Please input a number:+ read x3+ echo 'Please input another number:'Please input another number:+ read y4+ sum=7+ echo 'The sum of two numbers is:7'The sum of two numbers is:7[root@localhost ~]# |
在test4.sh中还有更加简洁的方式。
|
1
|
[root@localhost ~]# vi test5.sh |
|
1
2
3
4
5
6
|
#!/bin/bashread -p "Please input a number: " xread -p "Please input another number: " ysum=$[$x+$y]echo "The sum of two numbers is:$sum" |
read -p选项类似echo的作用。执行如下:
|
1
2
3
4
5
|
[root@localhost ~]# sh test5.shPlease input a number: 3Please input another number: 5The sum of two numbers is:8[root@localhost ~]# |
你有没有用过这样的命令“/etc/init.d/iptables restart”前面的/etc/init.d/iptables文件其实就是一个shell脚本,为什么后面可以跟一个“restart”?这里就涉及到了shell脚本的预设变量。实际上,shell脚本在执行的时候后边可以跟变量的,而且还可以跟多个。
|
1
|
[root@localhost ~]# vi test6.sh |
|
1
2
3
4
|
#!/bin/bashsum=$[$1+$2]echo $sum |
执行过程如下:
|
1
2
3
|
[root@localhost ~]# sh test6.sh 5 813[root@localhost ~]# |
在脚本中,你会不会奇怪,哪里来的$1和$2,这其实就是shell脚本的预设变量,其中$1的值就是在执行的时候输入的5,而$2的值就是执行的时候输入的8,当然一个shell脚本的预设变量是没有限制的,这回你明白了吧。另外还有一个$0,不过他代表的是脚本本身的名字。不妨把脚本修改一下。
|
1
|
[root@localhost ~]# vi test6.sh |
|
1
2
3
|
#!/bin/bashecho "$0 $1 $2" |
执行结果想必你也猜到了吧。
|
1
2
3
|
[root@localhost ~]# sh test6.sh 5 8test6.sh 5 8[root@localhost ~]# |
shell脚本中的逻辑判断
如果你学过C语言或者其他语言,相信你不会对if陌生,在shell脚本中我们同样可以使用if逻辑判断。在shell中if判断的基本语法为:
1)不带else
if 判断语句; then
command
fi
|
1
|
[root@localhost ~]# vi if1.sh |
|
1
2
3
4
5
6
|
#!/bin/bashread -p "Please input your score: " aif((a<60));then echo "you didn't pass the exam."fi |
在if1.sh中出现呢了((a<60))这样的形式,这是shell脚本中特有的格式,用一个小括号或者不用都会报错,请记住这个格式,即可。执行结果为:
|
1
2
3
4
|
[root@localhost ~]# sh if1.shPlease input your score: 36you didn't pass the exam.[root@localhost ~]# |
2)带有else
if 判断语句;then
command
else
command
fi
|
1
|
[root@localhost ~]# vi if2.sh |
|
1
2
3
4
5
6
7
8
|
#!/bin/bashread -p "Please input your score: " aif((a<60));then echo "you didn't pass the exam."else echo "Good! you passed the exam."fi |
执行结果为:
|
1
2
3
4
|
[root@localhost ~]# sh if2.shPlease input your score: 98Good! you passed the exam.[root@localhost ~]# |
3)带有elif
if 判断语句;then
command
elif 判断语句;then
command
else
command
fi
|
1
|
[root@localhost ~]# vi if3.sh |
|
1
2
3
4
5
6
7
8
9
10
|
#!/bin/bashread -p "Please input your score: " aif((a<60));then echo "you didn't pass the exam."elif ((a>60)) && ((a<85));then echo "Good! you passed the exam."else echo "Very good! your score is very high!"fi |
这里的&&表示“并且”的意思,当前你也可以使用||表示“或者”,执行结果:
|
1
2
3
4
5
6
7
8
9
10
|
[root@localhost ~]# sh if3.shPlease input your score: 98Very good! your score is very high![root@localhost ~]# sh if3.shPlease input your score: 50you didn't pass the exam.[root@localhost ~]# sh if3.shPlease input your score: 75Good! you passed the exam.[root@localhost ~]# |
以上只是简单的介绍了if语句的结构。在判断数字大小除了可以使用“(())”的形式外,还可以使用“[]”。但是就不能使用>,<,=这样的符号了,要使用-lt(小于)、-gt(大于)、-ge(大于等于)、-le(小于等于)、-eq(等于)、-ne(不等于)。
|
1
2
3
4
5
6
7
8
9
10
|
[root@localhost ~]# a=10;if [ $a -lt 5 ];then echo ok;fi[root@localhost ~]# a=10;if [ $a -gt 5 ];then echo ok;fiok[root@localhost ~]# a=10;if [ $a -ge 5 ];then echo ok;fiok[root@localhost ~]# a=10;if [ $a -le 5 ];then echo ok;fi[root@localhost ~]# a=10;if [ $a -eq 5 ];then echo ok;fi[root@localhost ~]# a=10;if [ $a -ne 5 ];then echo ok;fiok[root@localhost ~]# |
再看看if使用&&和||的情况。
|
1
2
3
4
|
[root@localhost ~]# a=10;if [ $a -lt 1 ] || [ $a -gt 10 ];then echo ok;fi[root@localhost ~]# a=8;if [ $a -gt 1 ] && [ $a -lt 10 ];then echo ok;fiok[root@localhost ~]# |
shell脚本中if还经常判断文档属性,比如判断是普通文件还是目录,判断文件是否有读写执行权限,常用的也就几个选项:
-e:判断文件或目录是否存在;
-d:判断是不是目录,并是否存在;
-f:判断是否是普通文件,并存在;
-r:判断文档是否有可读权限;
-w:判断文档是否有可写权限;
-x:判断是否可执行;
使用if判断时,具体格式为:if [-e filename];then
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[root@localhost ~]# if [ -d /home ];then echo ok;fiok[root@localhost ~]# if [ -f /home ];then echo ok;fi[root@localhost ~]# if [ -f test.txt ];then echo ok;fiok[root@localhost ~]# if [ -e test.txt ];then echo ok;fiok[root@localhost ~]# if [ -e test1.txt ];then echo ok;fi[root@localhost ~]# if [ -r test.txt ];then echo ok;fiok[root@localhost ~]# if [ -w test.txt ];then echo ok;fiok[root@localhost ~]# if [ -x test.txt ];then echo ok;fi[root@localhost ~]# |
在shell脚本中,除了用if来判断逻辑外,还有一种常用的方式,那就是case了。具体格式为:
case 变量 in
value1)
command
;;
vaule2)
command
;;
value3)
command
;;
*)
command
;;
esac
上面的结构中,不限制value的个数,*则代表除了上面的value外的其他值。下面笔者写了一个判断输入值时奇数或者偶数的脚本。
|
1
|
[root@localhost ~]# vi case.sh |
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/bin/bashread -p "Please input a number: " na=$[$n%2]case $a in 1) echo "The number is odd" ;; 0) echo "The number is even" ;;esac |
执行结果是:
|
1
2
3
4
5
6
7
|
[root@localhost ~]# sh case.shPlease input a number: 3The number is odd[root@localhost ~]# sh case.shPlease input a number: 8The number is even[root@localhost ~]# |
shell脚本中的循环
shell脚本中也算是一门简易的编程语言了,当然循环是不能缺少的。常用到的循环有for循环和while循环。
|
1
|
[root@localhost ~]# vi for.sh |
|
1
2
3
4
5
|
#!/bin/bashfor i in `seq 1 5`;do echo $idone |
脚本中的seq 1 5表示从1到5的一个序列。你可以直接运行这个命令试一下。脚本执行结果为:
|
1
2
3
4
5
6
7
|
[root@localhost ~]# sh for.sh12345[root@localhost ~]# |
通过这个脚本就可以看到for循环的基本结构:
for 变量名 in 循环条件;do
command
done
|
1
2
3
4
5
6
7
|
[root@localhost ~]# for i in 1 2 3 4 5;do echo $i;done12345[root@localhost ~]# |
循环的条件那一部分也可以写成这样的形式,中间用空格隔开即可。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
[root@localhost ~]# for i in `ls`;do echo $i;done1.txtanaconda-ks.cfgbreak2.shbreak.shcase1.shcase2.shcase.shcodecontinue2.shcontinue.shfor1.shfor2.shfor3.shfor.shfunction1.shfunction2.shif1.shif2.shif3.shmyfiletest1.shtest2.shtest3.shtest4.shtest5.shtest6.shtest.shtest.txtuntil.shuserswcwhile2.shwhile.sh[root@localhost ~]# |
再来看一下这个while循环,基本格式是:
while 条件;do
command
done
|
1
|
[root@localhost ~]# vi while.sh |
|
1
2
3
4
5
6
7
|
#!/bin/basha=10while [ $a -ge 1 ];do echo "$a" a=$[$a-1]done |
脚本执行结果为:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
[root@localhost ~]# sh while.sh10987654321[root@localhost ~]# |
shell脚本中的函数
如果你学过开发,肯定知道函数的作用。如果你是刚刚接触到这个概念的话,也没有关系,其实很好理解的。函数就是把一段带啊整理到一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可。有时候脚本中的某段代码总是重复使用,如果写成函数,每次用到时直接用函数名代替即可,这样就节省了时间还节省了空间。
|
1
|
[root@localhost ~]# vi fun.sh |
|
1
2
3
4
5
6
7
8
|
#!/bin/bashfunction sum(){ sum=$[$1+$2] echo $sum}sum $1 $2 |
fun.sh中的sum()为自定义的函数,在shell脚本中要用这样的格式去定义函数。
function 函数名(){
command
}
上个脚本的执行结果为:
|
1
2
3
|
[root@localhost ~]# sh fun.sh 5 813[root@localhost ~]# |
在shell脚本中,函数一定要在最前面,不能出现在中间或者最后,因为函数是要被调用的,如果还没出现就被调用,肯定是会出错的。
练习题
1、编写shell脚本,计算1-100的和;
|
1
2
3
4
5
6
7
8
|
#!/bin/bashsum=0for i in `seq 1 100`;do sum=$[$sum+$i]doneecho $sum |
2、编写shell脚本,要求输入一下数字,然后计算出从1到这个数字的和。要求,如果输入的数字小于1,则重新输入,知道输入正确的数字为止;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/bin/bashwhile :;do read -p "Input a number: " a sum=0 if ((a>1));then while [ $a -ge 1 ];do sum=$[$sum+$a] a=$[$a-1] done echo $sum break else continue fidone |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#!/bin/bashn=0while [ $n -lt 1 ];do read -p "Please input a number, it must greater than 1: " ndonesum=0for i in `seq 1 $n`;do sum=$[$sum+$i]doneecho $sum |
3、编写shell脚本,把/root/目录下的所有目录(只需要一级)拷贝到/tmp/目录下;
|
1
2
3
4
5
6
7
8
9
|
#!/bin/bashdir="/root/"for f in `ls $dir`;do tmp_dir=${dir}${f} if [ -d $tmp_dir ];then cp -r $tmp_dir /tmp/ fidone |
4、编写shell脚本,批量建立用户user_00,user_01,...,user_100并且所有用户属于users组;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/bin/bashgroup=usersegrep "^$group" /etc/group >& /dev/nullif [ $? -ne 0 ];then groupadd $groupfifor i in `seq 0 100`;do if [ $i -lt 10 ];then user="user_0${i}" else user="user_${i}" fi egrep "^$user" /etc/passwd >& /dev/null if [ $? -ne 0 ];then useradd -g $group $user fidone |
5、编写shell脚本,截取文件test.log中的包含关键词‘abc’的行中第一列(假设分隔符为“:”),然后把截取的数字排序(假设第一列为数字),然后打印出重复次数超过10次的列;
6、编写shell脚本,判断输入的IP是否正确(IP的规则是:n1.n2.n3.n4,其中1<n1<255,0<n2<255,0<n3<255,0<n4<255)。

浙公网安备 33010602011771号