目录
1为什么学习脚本
可以更加了解与控制Linux,使Linux动作更顺畅,而且可以高枕无忧地让你的Linux服务器在Internet上提供相关服务的话,那么应该把脚本学习好。
除了针对主机,脚本还有相当多的功能,例如计算,可以不必用编程语言来写,脚本本身是个可以使用的程序。例如:防火墙连续规则(iptables),启动加载程序项(/etc/rc.d/rc.local),均是脚本。
2脚本的执行
上一个实验中学习了一堆变量、管线指令等,都是为了给脚本做铺垫。将我们要执行的内容写成一个脚本,让系统依据这个脚本来执行我们想要的东西。基本上,执行脚本时,bash判断的步骤为:
1)如果读取到个Enter符号(CR),就尝试开始执行该命令;
2)如同前面bash command提到的,指令间的多个空白会被忽略;
3)空白行也将被忽略,并且Tab也不会被理会;
4)如果一行内容太多,则可以使用\延伸至下一行;
5)此外,使用最多的#可做为注释。任何加在#后面的字,将全部被视为注释文字而被忽略。
在撰写脚本时,最好养成如下良好的习惯:
1)先声明使用的shell为何(特别留意这一点,在某些情况下,例如/etc/crontab,如果没有声明使用的shell,常会出现错误信息而导致脚本无法被执行)。
2)注明该脚本的内容功能、版本信息、作者、文件创建日期等。
3)每一个大步骤的主要功能(也顺便供自己未来做修改之用)。
那么,如何执行脚本文件?执行的方法有两种:
-
-
-
-
-
-
-
- 一个是将文件改成可以执行的属性,如chmod 755 scripts.file,然后执行该文件;
- 另一种则是直接以sh这个执行文件来执行脚本内容,如sh scripts.file。
-
-
-
-
-
-
举例1:一个简单的脚本:在屏幕上显示Hello !How are you?,可以这样写(注:最常用来作为shell scripts的编写软件就是vi,有空要多熟悉vi):
# mkdir test; cd test //建立一个新的目录,所有的脚本都暂存在此!
# vi test01-hello.sh //为了醒目,脚本的文件扩展名为sh
#!/bin/bash //在#之后加上!与shell的名称,用来声明使用的shell
#这个脚本的用途在屏幕上显示Hello ! How are you
#创建日期:2007-4-9
#Written by xag
hello=Hello\\!\How\are\you\\? //这是定义变量,其中代表一个空格字符,由于Hello与!之间有空格,而这是一个整体字符串赋值所以使用\将这些字符连接起来赋值给变量hello
echo $hello
#sh test01-hello.sh //用sh来执行脚本
Hello ! How are you ? //输出结果
说明:黑色底纹部分为在vi里编辑的内容,汉字若输出不了,则可以使用英文来代替
这里注意:
- 所有在脚本里的东西,基本规则(如变量设定规则)需要与在命令行下相同;
- 脚本的后缀名最好为sh,以方便他人识别;
- 并非加上.sh就是可执行文件,还需要查看属性中是否有x属性
举例2:稍微复杂一点的脚本,若一个脚本里有两个以上的变量要相互引用,该如何做?
# vi test02-2var.sh
#!/bin/bash
#这个脚本用于引用两个变量,顺便比较一下"与'的异同
#Date:2007-4-9
#Written by xag
name="xag"
myname1="My name is $name"
myname2='My name is $name'
echo $name
echo $myname1
echo $myname2
#sh test02-2var.sh
xag
My name is xag
My name is $name
从这里可以看出,"与'最大的不同就在于能不能保存变量内容。再提醒一次,单引号“'”里的数据都将变成单纯的字符,而不是有特殊字体.
3卷标与运算符declare
变量的声明,不声明类型直接参与运算所得结果分析:
# a=3
# b=5
# c=$a*$b
# echo $c
3*5 //显示结果为什么不是15?怎么变成了字符串?
这是我们没有定义该变量,则该变量默认是呈现字符串的形式,自然$c变成为字符串形式了。故变量使用前应进行声明,用declare进行声明。
declare
格式:declare [-afirx] //参数说明:-a定义为数组array;-f定义为function;-i定义为整数integer;-r定义为只读;-x定义为通过环境输出变量
范例:
#declare -i a=3
#declare -i b=5
#declare -i c=$a*$b
#echo $c
//显示结果
4交互式脚本
最简单的交互式指令是read指令,read的功能就是将你在键盘上输入的内容放到变量中,如:
# read name
xag //这里直接在键盘上输入数据
# echo $name
xag //显示结果
例如:将你在键盘输入的数据显示出来
# vi test03-read.sh
#!/bin/bash
#This program is used to "read" variables
#xag 2007-4-9
echo "Please keyin your name, and press Enter to start."
read name
echo "This is your keyin data: $name"
#sh test03-read.sh
Please keyin your name, and press Enter to start.
xag
下面讲解如何定义脚本的参数代号?
格式:myscripts opt1 opt2 opt3 opt4 opt5 opt6 opt7 opt8 opt9
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9
上面的意思是说,在这个脚本(myscripts)中,只要用变量名称为$0,就表示myscripts,即:
- $0:myscripts,亦即脚本的文件名(像C中的main参数中的argv[0])
- $1:opt1,亦即第一个附加的参数
- $2:opt2,亦即第二个附加的参数
- $3:opt3,亦即第三个附加的参数
- ……
下面通过一个具体的例子说明:
# vi test04-0123
#!/bin/bash
#This program will define what is the parameters
#xag 2007-4-9
echo "This script's name: $0"
echo "parameters $1 $2 $3"
# sh test04-0123 pa1 pa2 pa3
This scipt's name: test04-0123
paramters pa1 pa2 pa3
这个参数用法在运用上很重要,例如当你要取得脚本的名称时(因为有时候用户会自行更改文件名称),这个功能变量就相当重要了。
5脚本逻辑判断式与表达式
1)逻辑判断式
几个重要的概念
逻辑卷标 | 含义 |
1. | 关于文件与目录的逻辑卷标 |
-f | 常用!检测文件是否存在 |
-d | 常用!检测目录是否存在 |
-b | 检测是否为一个block文件 |
-c | 检测是否为一个character文件 |
-S | 检测是否为一个socket标签文件 |
-L | 检测是否为一个符号连接文件 |
-e | 检测某个东西是否存在!可以是任何东西 |
2. | 关于程序的逻辑卷标 |
-G | 检测是否由GID所执行的程序拥有 |
-O | 检测是否由UID所执行的程序拥有 |
-P | 检测是否为程序间传送信息的name pipe或FIFO |
3. | 关于文件的属性检测 |
-r | 检测是否为可读属性 |
-w | 检测是否为可写入属性 |
-x | 检测是否为可执行属性 |
-s | 检测是否为非空白文件 |
-u | 检测是否为具有SUID属性 |
-g | 检测是否为具有SGID属性 |
-k | 检测是否具有sticky bit属性 |
4. | 两个文件之间的判断与比较。例如test file1 –nt file2 |
-nt | 第一个文件比第二个文件新 |
-ot | 第一个文件比第二个文件旧 |
-ef | 第一个文件与第二个文件为同一个文件(诸如链接文件) |
5. | 逻辑与(and)和或(or) |
&& | 逻辑与 |
|| | 逻辑或 |
2)运算符
bash shell scripts的运算符的加减乘除运算
运算符 | 含义 |
= | 等于 |
!= | 不等于 |
< | 小于 |
> | 大于 |
-eq | 等于 |
-ne | 不等于 |
-lt | 小于 |
-gt | 大于 |
-le | 小于或等于 |
-ge | 大于或等于 |
-a | 双方都成立(and) |
-o | 单方成立(or) |
-z | 空字符 |
-n | 非空字符 |
逻辑判断式与if…then…fi的关系密不可分,后面讲解判断式中最常用的语法。
6条件判断
1)if…then…fi
语法格式:if [ 条件判断一 ] && (||) [ 条件判断二 ]; then //if是起始,后面可以接若干个判断式,使用&&或||执行判断
elif [ 条件判断三 ] && (||) [ 条件判断四 ]; then //第二段判断,如果第一段不符合要求就转到此搜寻条件,执行第二段程序
else //当前两段都不符合时,就执行这段内容
fi //结束if then的条件判断
说明:
① 在[]中,只能有一个判断式;
② 在[]与[]之间,可以使用&&或||结合判断式;
③ 每一个独立的组件之间需要用空格键隔开。(这一点最容易犯错误,如[后有一空格,]前有一空格)
例:# vi test05-ifthen.sh
#!/bin/bash
#This program is used to study if then
#xag 2007-4-10
echo "Press y to continue"
read yn
if [ "$yn" = "y" ];then
echo "script is running…"
else
echo "STOP!"
fi
# sh test05-ifthen.sh
Press y to continue
y //你输入的内容
script is running…
# sh test05-ifthen.sh
Press y to continue
n //你输入的内容,注意这里=两边也应有空格
STOP!
程序的意思是输入y继续执行,否则停止。这里有个问题,如果输入的是Y,程序还是停止了,怎么办?这个时候需要用到||。程序如下:
# cp test05-ifthen.sh test06-ifthen.sh
# vi test06-ifthen.sh //在原来基础上修改
#!/bin/bash
#This program is used to study if then
#xag 2007-4-10
echo "Press y to continue"
read yn
if [ "$yn" = "y" ] || [ "$yn" = "Y" ];then
echo "script is running…"
else
echo "STOP!"
fi
此时程序执行时,输入y或Y程序都会继续执行。
例:脚本运行时必需带参数
# vi test07-ifthen.sh
#!/bin/bash
#set parameters in the if then
#需要加上hello这个参数才会正确显示
#xag 2007-4-11
if [ "$1" = "hello" ]; then
echo "Hello! How are you?"
elif [ "$1" = "" ]; then
echo "You MUST input parameters"
else
echo "The only accept parameter is hello"
fi
2)case…esac
语法格式:case 种类方式(string) in
种类方式一)
程序执行段
;; //种类方式一的结束符号
种类方式二)
程序执行段
;;
*)
echo "Usage: { 种类方式一 | 种类方式二 }" //列出可以利用的参数值
exit 1
esac //case的结束处
种类方式(string)的格式主要有两种:
- 直接输入:就是以“执行文件 + string”的方式执行(/etc/rc.d/init.d里的基本设定方式),string可以直接写成$1(在执行文件后直接加入第一个参数)
- 交互式:就是由屏幕输出可能的项,然后让用户输入,通常必须配合read variable,然后string写成$variable的格式。
举例:建立一个名为test08-case.sh的文件做个试验。假如共有3个选项,分别为one,two,three,并假设使用直接输入方式,则可以写成:
# vi test08-case.sh
#!/bin/bash
#program: Using case mode
#xag 2007-4-11
#content: I will use this program to study the case mode!
#1.pring this program
echo "This program will print your selection!"
case $1 in
one)
echo "your choice is one"
;;
two)
echo "your choice is two"
;;
three)
echo "your choice is three"
;;
*)
echo "Usage {one|two|three}" //列出可以使用的参数(如果用户输入错误的参数)
exit 1
esac
# sh test08-case.sh //执行结果显示没有相对应的参数
This program will print your selection!
Usage {one|two|three}
# sh test08-case.sh three
This program will print your selection!
Your choice is three
再看交互式的case的用法,对上面的例子做个修改
# vi test09-case.sh
#!/bin/bash
#program: Using case mode
#xag 2007-4-11
#content: I will use this program to study the case mode!
#1.pring this program
echo "Press your select one ,two ,three"
read number
case $number in
one)
echo "your choice is one"
;;
two)
echo "your choice is two"
;;
three)
echo "your choice is three"
;;
*)
echo "Usage {one|two|three}" //列出可以使用的参数(如果用户输入错误的参数)
exit 1
esac
# sh test09-case.sh
Press your select one ,two ,three
Two //这一行是你输入的内容
your choice is two
7循环
最简单的循环语句如下:
-
- for((条件1;条件2;条件3))
- for variable in variable1 variable2……
- while [ condition1 ] && {||} [ condition2 ]…
- until [ condition1 ] && {||} [ condition2 ]…
for 是已经知道要运行几次,至于until与while则分别是:
-
- until:直到条件符合的时候才退出程序;
- while:当条件符合时,就继续做。
举例1:利用for计算1+2+3+…+100,用脚本如何写?有很多方式,我们来谈谈do…done。
#vi test10-loop.sh
#!/bin/bash
#Using for and loop
#xag 2007-4-12
declare –i s #变量声明
for ( ( i=1;i<=100;i=i+1))
do
s=s+i
done
echo "The count is :$s"
# sh test10-loop.sh
The count is :5050
请注意,for((条件1;条件2;条件3))是必要的。
-
- 条件1:可以看成是初始值,如例子中,初始值是i=1。
- 条件2:可以看成是终止值,如例子中,当i<=100时就给出程序停止运行的条件。
- 条件3:可以看成是步长,也就是说,i每次加1。
上面的例子是说:由i=1开始到i=100,每次i都加1来执行下面的程序段(就是s=s+i),当i>100(也就是i=101),就跳出这段程序。
举例2:使用while或until来实现上例。
(1)使用while:
# vi test11-loop.sh
#!/bin/bash
#Using while and loop
#xag 2007-4-12
declare –i i
declare –i s
while [ "$i" != "101" ]
do
s=s+i
i=i+1
done
echo "The count is:$s"
(2)使用until:
# vi test12-loop.sh
#!/bin/bash
#Using until and loop
#xag 2007-4-12
declare –i i
declare -i s
until [ "$i" = "101" ]
do
s=s+i
i=i+1
done
echo "The count is:$s"
# sh test11-loop.sh //若出错请使用bash test11-loop.sh
The count is:5050
举例3:下面的例子是另外一种循环方式,可以用来判断非数字类型。
# vi test13-for.sh
#!/bin/bash
#Using for … do …done
#xag 2007-4-12
LIST="Tomy Jony Mary Geoge"
for i in $LIST
do
echo $i
done
# sh test13-for.sh
Tomy
Jony
Mary
Geoge
这种格式是以空格键分开i变量的各选择项,也就是说,上面的$LIST变量中,以空格键分隔共可以分离出4个项,所以,使用do…done就可以分别输出这4个项。那么,有没有办法利用这个特性将Linux主机帐号显示出来?很简单,利用cut跟sort及/etc/passwd文件来完成这个脚本,如下:
# vi test14-for.sh
#!/bin/bash
#Using for and loop to read the account of this linux server!
#xag 2007-4-12
account=`cut –d ":" –f1 /etc/passwd |sort `
echo "The following is linux server's account"
for i in $account
do
echo $i
done
# sh test14-for.sh
The following is linux server's account
adm
aerosol
alenchan
amanda
apache
……
举例4:接下来我们以交互式实现循环功能,即当我们输入y或Y时,程序就结束。可以这样做:
# vi test15-loop.sh
#!/bin/bash
#Using until
#xag 2007-4-12
echo "Press Y/y to stop"
until [ "$yn" = "y" ] || [ "$yn" = "Y" ]
do
read yn
done
echo "Stop here"
# sh test15-loop.sh
Press Y/y to stop //下面的几行除了Stop here之外,都是你自己随意输入的内容:
GDSG
A
Y
Stop here
上面说的是,当输入Y或y时才跳出do…done循环,而去执行下面的东西。
举例5:接下来,我们来判断目录是否存在,这是常用的。看看所谓的逻辑判断式的使用方法。可以使用条件判断来断定到底有没有文件(用-e)或者该名称是属于目录还是文件(-d,-f),接下来我们判断一个流程:
-
- 先查看~/test/logical这个名称是否存在;
- 若不存在,则使用touch建立一个文件,建立完成后退出;
- 如果存在,判断该名称是否为文件,若为文件则将之删除后建立一个目录,文件名为logical,之后退出;
- 如果存在,而且该名称为目录,则移除此目录。
看起来似乎很复杂,其实很简单。如下:
# vi test16-ifthen.sh
#!/bin/bash
#Using if and then to select file or directory
#xag 2007-4-12
if [ ! –e logical ];then
touch logical
echo "Just make a file logical"
elif [ -e logical ] && [ -f logical ];then
rm logical
mkdir logical
echo "remove file:logical"
echo "and make directory logical"
exit 1
elif [ -e logical ] && [ -d logical ];then
rm -rf logical
echo "remove directory:logical"
else
echo "Does here have anything?"
fi
然后请你依次执行sh test16-ifthen.sh ; ll 看看这个目录下logical文件有什么变化。这个动作可以让我们很轻松地判别出某个文件是否存在。
8如何调试脚本
脚本在执行之前,最怕的就是出现问题,那么如何调试脚本?有没有办法不需要直接执行该脚本就可以判断是否有问题?当然有,我们就直接以sh进行判断。
sh语法格式:sh [-nvx] scripts
-n:不执行脚本,查询脚本内的语法,若有错误则列出
-v:在执行脚本之前,先将脚本的内容显示在屏幕上;
-x:将用到的脚本内容显示在屏幕上,与-v稍微不同。
如:
# sh -n test01-hello.sh
# sh -v test01-hello.sh
#!/bin/bash
#这个脚本的用途在屏幕上显示Hello ! How are you
#创建日期:2007-4-10
#Written by xag
hello=Hello\\!\How\are\you\\?
echo $hello
Hello ! How are you
# sh –x test01-hello.sh
+ hello=Hello ! How are you
+ echo 'Hello !' How are you
Hello ! How are you
熟悉sh的用法,可以使你在管理Linux的过程中得心应手。
对于Shell Scripts的学习方法,需要多看、多模仿,并将其修改为已用,这是最快的学习手段。网上有很多人在开发一些很有用的脚本,可以将对方的脚本拿来,并且改成适合自己主机的脚本,那么学习效果会更好。