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脚本的基本结构以及如何执行
[root@localhost ~]# vi test.sh
#!/bin/bash
# This is my first shell script.
date
echo "Hello World!"
shell脚本通常都是以.sh为后缀名的,这个并不是说不带.sh这个脚本就不能执行,只是大家的一个习惯而已。所以,以后你发现了.sh为后缀的文件那么它一定会是一个shell脚本了。test.sh中第一行“#!/bin/bash”它代表的意思是,该文件使用的是bash语法。如果不设置该行,那么你的shell脚本就不能执行。“#”表示注释,后面跟一些脚本相关的注释内容。当然这些注释并非必须的,如果你懒得很,可以省略,但是笔者不建议省略。因为随着你工作时间的增加,你写的shell脚本会越来越多,如果有一天你回头查看你写的某个脚本时,很可能忘记该脚本时用来干什么的以及什么时候写的。所以写上注释是有必要的。
[root@localhost ~]# sh test.sh
Wed Sep 5 23:33:17 CST 2018
Hello World!
[root@localhost ~]#
shell脚本的执行很简单,直接“sh filename”即可,另外你还可以这样执行。
[root@localhost ~]# chmod +x test.sh
[root@localhost ~]# ./test.sh
Wed Sep 5 23:34:55 CST 2018
Hello World!
[root@localhost ~]#
默认我们用vim编辑的文档是不带有执行权限的,所以需要加一个执行权限,那样就可以直接使用“./filename”执行这个脚本了。另外使用sh命令去执行一个shell脚本的时候是可以加-x选项来查看这个脚本执行过程的,这样有利于我们调试这个脚本哪里出问题了。
[root@localhost ~]# sh -x test.sh
+ date
Wed Sep 5 23:38:44 CST 2018
+ echo 'Hello World!'
Hello World!
[root@localhost ~]#
该shell脚本中用到了“date”这个命令,它的作用就是来打印当前系统的时间。其实在shell脚本中date使用率非常高。有几个选项笔者常常在shell脚本中用到:
[root@localhost ~]# date "+%Y %m %d %H:%M:%S"
2018 09 06 00:20:31
[root@localhost ~]#
%Y表示年,%m表示月,%d表示日期,%H表示小时,%M表示分钟,%S表示秒
[root@localhost ~]# date "+%y %m %d"
18 09 06
[root@localhost ~]#
注意%Y和%y的区别。
[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脚本中的变量
[root@localhost ~]# vi test2.sh
#!/bin/bash
d=`date +%H:%M:%S`
echo "the script begin at $d"
echo "now we will sleep 2 seconds."
sleep 2
d1=`date +%H:%M:%S`
echo "the script end at $d1"
在test2.sh中使用到了反引号,你是否还记得它的作用?“d”和“d1”在脚本中作为变量出现,定义变量的格式为“变量名=变量值”。当在脚本中引用变量时需要加上“$”符号。下面看看脚本执行的结果吧。
[root@localhost ~]# sh test2.sh
the script begin at 00:43:40
now we will sleep 2 seconds.
the script end at 00:43:42
[root@localhost ~]#
下面我们用shell计算两个数的和。
[root@localhost ~]# vi test3.sh
#!/bin/bash
a=1
b=2
sum=$[$a+$b]
echo "sum is $sum"
数学计算药用“[]”括起来并且外面要带一个“$”。脚本结果为:
[root@localhost ~]# sh test3.sh
sum is 3
[root@localhost ~]#
shell脚本还可以和用户交互。
[root@localhost ~]# vi test4.sh
#!/bin/bash
echo "Please input a number:"
read x
echo "Please input another number:"
read y
sum=$[$x+$y]
echo "The sum of two numbers is:$sum"
这就用到了read命令了,它可以从标准输入获得变量的值,后跟变量名。“read x”表示x变量的值需要用户通过键盘输入的到。脚本执行过程如下:
[root@localhost ~]# sh test4.sh
Please input a number:
5
Please input another number:
6
The sum of two numbers is:11
[root@localhost ~]#
我们不妨加上-x选项再来看一下这个执行过程:
[root@localhost ~]# sh -x test4.sh
+ echo 'Please input a number:'
Please input a number:
+ read x
3
+ echo 'Please input another number:'
Please input another number:
+ read y
4
+ sum=7
+ echo 'The sum of two numbers is:7'
The sum of two numbers is:7
[root@localhost ~]#
在test4.sh中还有更加简洁的方式。
[root@localhost ~]# vi test5.sh
#!/bin/bash
read -p "Please input a number: " x
read -p "Please input another number: " y
sum=$[$x+$y]
echo "The sum of two numbers is:$sum"
read -p选项类似echo的作用。执行如下:
[root@localhost ~]# sh test5.sh
Please input a number: 3
Please input another number: 5
The sum of two numbers is:8
[root@localhost ~]#
你有没有用过这样的命令“/etc/init.d/iptables restart”前面的/etc/init.d/iptables文件其实就是一个shell脚本,为什么后面可以跟一个“restart”?这里就涉及到了shell脚本的预设变量。实际上,shell脚本在执行的时候后边可以跟变量的,而且还可以跟多个。
[root@localhost ~]# vi test6.sh
#!/bin/bash
sum=$[$1+$2]
echo $sum
执行过程如下:
[root@localhost ~]# sh test6.sh 5 8
13
[root@localhost ~]#
在脚本中,你会不会奇怪,哪里来的$1和$2,这其实就是shell脚本的预设变量,其中$1的值就是在执行的时候输入的5,而$2的值就是执行的时候输入的8,当然一个shell脚本的预设变量是没有限制的,这回你明白了吧。另外还有一个$0,不过他代表的是脚本本身的名字。不妨把脚本修改一下。
[root@localhost ~]# vi test6.sh
#!/bin/bash
echo "$0 $1 $2"
执行结果想必你也猜到了吧。
[root@localhost ~]# sh test6.sh 5 8
test6.sh 5 8
[root@localhost ~]#
shell脚本中的逻辑判断
如果你学过C语言或者其他语言,相信你不会对if陌生,在shell脚本中我们同样可以使用if逻辑判断。在shell中if判断的基本语法为:
1)不带else
if 判断语句; then
command
fi
[root@localhost ~]# vi if1.sh
#!/bin/bash
read -p "Please input your score: " a
if((a<60));then
echo "you didn't pass the exam."
fi
在if1.sh中出现呢了((a<60))这样的形式,这是shell脚本中特有的格式,用一个小括号或者不用都会报错,请记住这个格式,即可。执行结果为:
[root@localhost ~]# sh if1.sh
Please input your score: 36
you didn't pass the exam.
[root@localhost ~]#
2)带有else
if 判断语句;then
command
else
command
fi
[root@localhost ~]# vi if2.sh
#!/bin/bash
read -p "Please input your score: " a
if((a<60));then
echo "you didn't pass the exam."
else
echo "Good! you passed the exam."
fi
执行结果为:
[root@localhost ~]# sh if2.sh
Please input your score: 98
Good! you passed the exam.
[root@localhost ~]#
3)带有elif
if 判断语句;then
command
elif 判断语句;then
command
else
command
fi
[root@localhost ~]# vi if3.sh
#!/bin/bash
read -p "Please input your score: " a
if((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
这里的&&表示“并且”的意思,当前你也可以使用||表示“或者”,执行结果:
[root@localhost ~]# sh if3.sh
Please input your score: 98
Very good! your score is very high!
[root@localhost ~]# sh if3.sh
Please input your score: 50
you didn't pass the exam.
[root@localhost ~]# sh if3.sh
Please input your score: 75
Good! you passed the exam.
[root@localhost ~]#
以上只是简单的介绍了if语句的结构。在判断数字大小除了可以使用“(())”的形式外,还可以使用“[]”。但是就不能使用>,<,=这样的符号了,要使用-lt(小于)、-gt(大于)、-ge(大于等于)、-le(小于等于)、-eq(等于)、-ne(不等于)。
[root@localhost ~]# a=10;if [ $a -lt 5 ];then echo ok;fi
[root@localhost ~]# a=10;if [ $a -gt 5 ];then echo ok;fi
ok
[root@localhost ~]# a=10;if [ $a -ge 5 ];then echo ok;fi
ok
[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;fi
ok
[root@localhost ~]#
再看看if使用&&和||的情况。
[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;fi
ok
[root@localhost ~]#
shell脚本中if还经常判断文档属性,比如判断是普通文件还是目录,判断文件是否有读写执行权限,常用的也就几个选项:
-e:判断文件或目录是否存在;
-d:判断是不是目录,并是否存在;
-f:判断是否是普通文件,并存在;
-r:判断文档是否有可读权限;
-w:判断文档是否有可写权限;
-x:判断是否可执行;
使用if判断时,具体格式为:if [-e filename];then
[root@localhost ~]# if [ -d /home ];then echo ok;fi
ok
[root@localhost ~]# if [ -f /home ];then echo ok;fi
[root@localhost ~]# if [ -f test.txt ];then echo ok;fi
ok
[root@localhost ~]# if [ -e test.txt ];then echo ok;fi
ok
[root@localhost ~]# if [ -e test1.txt ];then echo ok;fi
[root@localhost ~]# if [ -r test.txt ];then echo ok;fi
ok
[root@localhost ~]# if [ -w test.txt ];then echo ok;fi
ok
[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外的其他值。下面笔者写了一个判断输入值时奇数或者偶数的脚本。
[root@localhost ~]# vi case.sh
#!/bin/bash
read -p "Please input a number: " n
a=$[$n%2]
case $a in
1)
echo "The number is odd"
;;
0)
echo "The number is even"
;;
esac
执行结果是:
[root@localhost ~]# sh case.sh
Please input a number: 3
The number is odd
[root@localhost ~]# sh case.sh
Please input a number: 8
The number is even
[root@localhost ~]#
shell脚本中的循环
shell脚本中也算是一门简易的编程语言了,当然循环是不能缺少的。常用到的循环有for循环和while循环。
[root@localhost ~]# vi for.sh
#!/bin/bash
for i in `seq 1 5`;do
echo $i
done
脚本中的seq 1 5表示从1到5的一个序列。你可以直接运行这个命令试一下。脚本执行结果为:
[root@localhost ~]# sh for.sh
1
2
3
4
5
[root@localhost ~]#
通过这个脚本就可以看到for循环的基本结构:
for 变量名 in 循环条件;do
command
done
[root@localhost ~]# for i in 1 2 3 4 5;do echo $i;done
1
2
3
4
5
[root@localhost ~]#
循环的条件那一部分也可以写成这样的形式,中间用空格隔开即可。
[root@localhost ~]# for i in `ls`;do echo $i;done
1.txt
anaconda-ks.cfg
break2.sh
break.sh
case1.sh
case2.sh
case.sh
code
continue2.sh
continue.sh
for1.sh
for2.sh
for3.sh
for.sh
function1.sh
function2.sh
if1.sh
if2.sh
if3.sh
myfile
test1.sh
test2.sh
test3.sh
test4.sh
test5.sh
test6.sh
test.sh
test.txt
until.sh
users
wc
while2.sh
while.sh
[root@localhost ~]#
再来看一下这个while循环,基本格式是:
while 条件;do
command
done
[root@localhost ~]# vi while.sh
#!/bin/bash
a=10
while [ $a -ge 1 ];do
echo "$a"
a=$[$a-1]
done
脚本执行结果为:
[root@localhost ~]# sh while.sh
10
9
8
7
6
5
4
3
2
1
[root@localhost ~]#
shell脚本中的函数
如果你学过开发,肯定知道函数的作用。如果你是刚刚接触到这个概念的话,也没有关系,其实很好理解的。函数就是把一段带啊整理到一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可。有时候脚本中的某段代码总是重复使用,如果写成函数,每次用到时直接用函数名代替即可,这样就节省了时间还节省了空间。
[root@localhost ~]# vi fun.sh
#!/bin/bash
function sum(){
sum=$[$1+$2]
echo $sum
}
sum $1 $2
fun.sh中的sum()为自定义的函数,在shell脚本中要用这样的格式去定义函数。
function 函数名(){
command
}
上个脚本的执行结果为:
[root@localhost ~]# sh fun.sh 5 8
13
[root@localhost ~]#
在shell脚本中,函数一定要在最前面,不能出现在中间或者最后,因为函数是要被调用的,如果还没出现就被调用,肯定是会出错的。
练习题
1、编写shell脚本,计算1-100的和;
#!/bin/bash
sum=0
for i in `seq 1 100`;do
sum=$[$sum+$i]
done
echo $sum
2、编写shell脚本,要求输入一下数字,然后计算出从1到这个数字的和。要求,如果输入的数字小于1,则重新输入,知道输入正确的数字为止;
#!/bin/bash
while :;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
fi
done
#!/bin/bash
n=0
while [ $n -lt 1 ];do
read -p "Please input a number, it must greater than 1: " n
done
sum=0
for i in `seq 1 $n`;do
sum=$[$sum+$i]
done
echo $sum
3、编写shell脚本,把/root/目录下的所有目录(只需要一级)拷贝到/tmp/目录下;
#!/bin/bash
dir="/root/"
for f in `ls $dir`;do
tmp_dir=${dir}${f}
if [ -d $tmp_dir ];then
cp -r $tmp_dir /tmp/
fi
done
4、编写shell脚本,批量建立用户user_00,user_01,...,user_100并且所有用户属于users组;
#!/bin/bash
group=users
egrep "^$group" /etc/group >& /dev/null
if [ $? -ne 0 ];then
groupadd $group
fi
for 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
fi
done
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)。