Shell程序是通过文本编辑程序把一系列Linux命令放在一个文件中执行的实用程序。执行Shell程序时,文件中的Linux命令会被一条接一条地边解释边执行。因此,当用户需要通过多个Linux命令地执行才能完成最后地操作时,可以利用Shell程序把这些Linux命令几种在一个文件中,通过文件地执行快速地得到最后地结果。Shell编程语言和其他高级编程语言一样,也可以利用自己地语法定义变量并赋值,使用各种控制结构,设计出符合用户需要地程序。
初体验:打印彩虹:
declare -a ary
for i in `seq 40 49`
do
ary[$i]=" "
echo -en "\e[$i;5m ${ary[@]}\e[;0m"
done
declare -a ary
for s in `seq 1 10000`
do
for i in `seq 40 49`
do
ary[$i]=" "
echo -en "\e[$i;5m ${ary[@]}\e[;0m"
done
done
Shell脚本也成为Shell程序,它是由一系列Shell命令构成地文本文件。Shell程序可以是一些列Shell命令地组合,也可以利用各种程序控制结构(分支结构、循环结构等)编写复杂的脚本程序。总之,Shell脚本是用户根据需要编写的用于解决一系列问题的程序。
建立Shell脚本的方法有很多,由于Shell脚本是文本文件,所以可以使用文本编辑器建立和编辑脚本,常用的文本编辑器有vi、emacs、Gedit等。
首先,通过文本编辑器完成一个简单的Shell脚本编写。打开文本编辑器,输入下列程序:
#! /bin/bash
# this is my first shell script
echo "Hello World!!1"
date
保存该文件,将文件命名为helloworld,这样就建立了以恶名为helloworld的脚本文件。纵览该文件可以发现,Shell脚本的编写方法非常简单,各行的含义如下:
#
符号,代表后面的字符都是注释;echo
命令输出一行字符:‘helloworld’;1.赋予脚本文件可执行的权限,改变脚本文件的属性:
chmod 755 文件名
或
chmod a+x 文件名
2.文件具有可执行权限后就可以执行了,在文件所在的目录输入:
./文件名
或
bash 文件名
或
. 文件名
或
source 文件名
在高级语言 程序设计中,编写交互式程序是提高程序可读性、实用性的一个时分常用的手段。交互式程序允许用户在程序的执行过程中输入数据,程序会根据用户输入的数据进行响应的操作。
交互式Shell脚本的执行也与上述情况类似,因此,学习交互式Shell脚本的编写时学习Shell脚本设计必不可少的一环。Shell脚本中需要使用read
命令读取用户输入的值(字符串),记录在变量中。
read
命令的格式如下:
read [-p "字符串"] 变量名
例如,编写一个交互式Shell脚本,要求用户输入一行字符串,然后将此字符串显示出来。脚本如下所示:
上图脚本利用name变量记住用户交互式输入的用户名,再利用echo命令输出name变量的值。后中断交互式脚本,在执行脚本,用户输入名字后并可显示出来。
test
命令可以对表达式的执行结果进行判断。表达式可以包括文件、整数、字符串。但是test
命令执行结束时并不显示任何判断结果,而是用返回值来表示判断的结果。返回值为0时,表示判断结果为真;返回值为1时,表示判断结果为假。test
命令的判断结果主要用于程序控制结构(如if
语句)中的条件判断。
test
命令格式如下:
test 表达式 或者 [表达式]
使用test
命令时应注意:
"["
和"]"
两侧都要有空格;\
使其失去特殊含义,不被Shell解释执行; test
命令的常用表达式有文件判断、整数判断、字符串判断、逻辑判断。
test
命令主要用于检验一个文件的类型、属性、比较两个值。文件判断操作符可以用来进行文件的比较,如下表所示:
操作符号 | 含义 | 操作符号 | 含义 |
---|---|---|---|
-d | 确定文件是否为目录 | -e | 确定文件是否存在 |
-f | 确定文件是否为普通文件 | -w | 确定是否对文件设置了写权限 |
-r | 确定是否对文件设置了读权限 | -x | 确定是否对文件设置了执行权限 |
-s | 确定文件名长度是否大于0 |
test -f /etc/passwd # 判断文件是否为一个普通文件
echo $? # 输出结果
test -r /etc/passwd # 判断文件是否具有读权限
echo $?
整数判断操作符可以用来比较两个整数,如下标所示:
操作符号 | 含义 | 操作符号 | 含义 |
---|---|---|---|
-eq | 比较两个整数是否相等 | -ne | 比较两个整数是否不等 |
-ge | 比较两个整数是否大于或等于另一个整数 | -gt | 比较一个整数是否大于另一个整数 |
-le | 比较一个整数是否小于或等于另一个整数 | -lt | 比较一个整数是否小于另一个整数 |
例如,利用整数判断操作符对整数进行比较。通过test
命令判断两个整数30和90是否相等,结果为假,返回1.再判断30和90是否不等,结果为真,返回值为0.
字符串判断操作符可以用来比较两个字符串表达式,如下表所示:
操作符号 | 含义 | 操作符号 | 含义 |
---|---|---|---|
= | 比较两个字符串是否相等 | -z | 判断字符串长度是否等于0 |
!= | 比较两个字符串是否不相等 | -n | 判断字符串长度是否大于0 |
定义一个name变量,并赋值为user。用test
命令判断name变量的值是否等于user,结果为真,返回值为0。注意,在test
命令中,等号两边都要保留空格。
以下示例判断字符串是否含有空格:给name变量赋予一个带空格的字符串user zhang,赋值时应该把这个字符串整体用双引号括起来,即引号内的所有字符串作为一个整体来处理。用test
命令进行判断的时候也应该注意这个问题,对name变量的取值也要加双引号,即"$name",然后再进行判断。
test
可以对带有逻辑运算符的表达式进行判断。逻辑判断就是要测试带有逻辑运算符的表达式的结果(为真或者假)。常用的逻辑运算符有与(and)、或(or)、非(not),在shell中分别用-a、-o
或!
表示。
逻辑判断一般和文件判断、字符串判断、整数判断结合使用。以下是进行逻辑判断的test命令示例:
以上表达式可以分为3个部分来理解:
-f /etc/passwd
是判断该文件是不是一个普通文件;-r /etc/passwd
是判断该文件是不是具有只读的属性;-a
是1,2两部分的结合,是“与”判断,是“并且”的意思,即1,2是不是同时成立。test
命令中的表达式的含义是判断( $a=4 or $b>20 \) and ($c<=100)
是否为真,再用$?待会返回值,结果判断为真,返回0。
Shell脚本和其他语言类似,在分支结构中也提供了分支结构和循环结构两大类。分支结构包括if
语句和case
语句。
在Shell中任何命令都有返回状态,因此,都可以作为if
语句的条件使用。此外,在Shell中还提供了true
命令,false
命令以及:
命令。他们只返回一个特定的值::
和true
命令返回值为0,false
命令返回值为非0。
if
语句的语法格式如下:
if [条件命令]
then
命令1
命令2
...
fi
或者
if [条件命令]; then
命令1
命令2
...
fi
说明:
then
和fi
之间的命令;如果返回值为非0,则不执行这些命令;创建脚本文件,内容如下:
#! /bin/bash
read -p "input your name:" name
if [ $name = "user" ]; then
echo "hello $name"
fi
修改文件权限:
chmod 755 文件名
执行文件:
sh 文件名
if-else
结构是一种双分支选择,如果条件命令的返回值为0,执行命令列表1;如果返回值为非0,则执行命令列表2.
if-else
结构的语法格式如下:
if [ 条件命令 ]; then
命令列表1
else
命令列表2
fi
创建脚本文件,内容如下:
#! /bin/bash
read -p "input your name:" name
if [ $name = "user" ]; then
echo "hello $name"
else
echo "sorry,your name is not right"
fi
修改文件权限:
chmod 755 文件名
执行文件:
sh 文件名
利用if-elif-else
结构进行多分支判断,语法格式如下:
if [ 条件命令1]; then
命令列表1
elif [ 条件命令2 ]; then
命令列表2
else
命令列表3
fi
创建脚本文件,内容如下:
#! /bin/bash
read -p "input your score:" score
if [ $score -lt 60 ]; then
echo "not pass"
elif [ $score -ge 60 -a $score -le 70 ]; then
echo "pass -D"
elif [ $score -ge 70 -a $score -le 80 ]; then
echo "pass -C"
elif [ $score -ge 80 -a $score -le 90 ]; then
echo "pass -B"
else
echo "pass -A"
fi
修改文件权限:
chmod 755 文件名
执行脚本:
sh 文件名
嵌套if语句的语法格式如下:
if [ 条件命令1 ]; then
if [ 条件命令2 ]; then
命令列表1
else
命令列表2
fi
else
命令列表3
fi
编写脚本程序,验证用户名和密码是否正确:
#! /bin/bash
read -p "input your name:" name
if [ $name = "user" ]; then
read -p "input your password:" password
if [ $password = "123456" ]; then
echo "hello $name"
else
echo "your password is not right"
fi
else
echo "sorry,your name is not right"
fi
修改文件权限:
chmod 755 文件名
执行脚本:
sh 文件名
case
语句是多分支语句,用于进行多路条件测试,可用来替换if-elif-else
语句,但它的结构更加清晰。其语法格式如下:
case 变量值 in
模式1)
命令列表1;
模式2)
命令列表2;
...
*)
命令列表n;
esac
case
语句执行时,先将变量值与各个模式依次比较,如果有一个相匹配,则执行该模式下的命令列表。
**注意:**如果有多个匹配模式,只执行第一个匹配模式下的命令列表;如果没有匹配的模式,则执行"*)"下的命令列表。
编写脚本程序,根据时间现实问候:
hour=`date +%H`
case $hour in
08 | 09 | 10 | 11 | 12) echo "Good Morning!";;
12 | 14 | 15 | 16 | 17) echo "Good Afternoon!";;
18 | 19 | 20 | 21 | 22) echo "Good Evening!";;
*) echo "Good Night";
esac
修改文件权限:
chmod 755 文件名
执行文件:
sh 文件名
循环结构用来控制某个处理过程的重复执行。Shell提供了3中循环结构:for循环、while循环、until循环。
for循环用于处理简单的、次数确定的循环过程。语法格式如下:
for 变量 [in 字符串列表]
do
命令列表
done
for循环要先定义一个变量,它依次取in后面字符串列表中各个字符串作为其值。对每个取值,都执行for循环内部的命令列表。因此,字符串列表中的字符串的个数就代表了for循环的执行次数。
编写脚本,要求用户输入一个目录,然后判断此目录下的文件有哪些具有读权限:
#! /bin/bash
read -p "please input a directory:" dire
if [ -e $dire -a -d Sdire ]; then
file=`ls $dire`
for filename in $ file
do
if [ -r $dire/$filename ]; then
echo "$filename can be read"
ls -l $dire/$filename
fi
done
else
echo "sorry,$dire can not exists!"
fi
修改文件权限:
chmod 755 文件名
执行脚本:
sh 文件名
while循环是当条件为真时进入循环体,执行命令列表,直到条件为假时退出循环。语法格式为:
while [ 条件 ]
do
命令列表
done
**案例1:**编写脚本,用while循环判断用户登录时的用户名是否正确,直至正确为止:
#! /bin/bash
while [ 1 ]
do
read -p "input username:" username
if [ $username = "micheal" ]; then
echo "hello $username"
break
fi
echo "sorry, name failed!"
done
修改文件权限
chmod 755 文件名
执行脚本
sh 文件名
**案例2:**利用while循环输入5个数,累加求和。while循环的执行条件时loopcount变量值小于5。
#! /bin/bash
loopcount=0
result=0
while [ $loopcount -lt 5 ]
do
read -p "input a number:" num
declare -i loopcount = $loopcount + 1
declare -i result = $result + $num
done
echo "result is $result"
修改权限:
chmod 755 文件名
执行脚本:
sh 文件名
注意:上述脚本会报一个错误:test11.sh: 7: declare: not found
,这是因为ubuntu系统的问题,脚本本身没有问题。
until循环在条件为假时进入循环体,执行命令列表,直至条件为真时才退出循环。语法格式如下:
until [ 条件 ]
do
命令列表
done
编写脚本,判断用户登陆时的用户名是否正确,直至用户名正确为止:
#! /bin/bash
read -p "login name:" username
until [ $username='micheal' ]
do
echo "sorry,name failed!"\
read -p "login name:" username
done
echo "hello $username"
修改权限:
chmod 755 文件名
执行脚本:
sh 文件名
break和continue可以用户循环结构的控制,并只能用于for循环、while循环、until循环中。
他们的区别是:break命令使程序的执行退出整个循环结构;而continue命令使程序跳出本次循环,进入下一次循环,并不退出整个循环结构。
示例:用break退出循环结构。用户输入一个数,小于13输出这个数,其他则输出aaaa:
#/bin/bash
read -p "please input a number: " i
if [ $i -lt 3 ]
then
echo $i
break
else
echo aaaa
fi
示例:break用户中断当前循环,输出1-5之间的数,遇到3终止当前输出,继续下次循环:
#/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
continue
fi
echo $i
done
在Shell变成中,用户可以编制自己的函数,函数可以简化程序,使程序的结构更加清晰,而且可以避免程序中重复编写某些代码的工作。因此,当某一段代码会在程序中被经常使用或重复执行时,可以把这些代码以函数的形式来表示,并给函数起一个名字。以后再用到这些代码时,就可以直接使用函数名来调用这些代码了。
函数的定义格式如下:
function 函数名() {
命令列表
}
函数的命名规则与变量的命令规则相同。调用函数的时候直接使用函数名。
编写函数func,该函数的任务是现实当前的日历、日期和工作路径,并显示一行字符串。要执行这个函数,只需要调用函数名func即可。
#! /bin/bash
function func()
{
cal
date
ped # 将pwd写成ped,出错后期调试用
echo "this is function!"
}
# 调用两次函数
func
func
注意:脚本本身没有问题,但有可能会报Syntax error: "(" unexpected
,可排查下版本等问题,或将执行命令改为:
bash -x 脚本名
编写万Shell脚本后,可以在Shell中进行脚本的调试。调试程序对于程序员来说是一项必不可少的工作。调式脚本的命令是bash
,命令格式如下:
bash [选项] 脚本名
常用选项有:
-x
:在执行脚本是现实脚本的内容,以方便用户追踪调试程序-n
:只检查脚本的语法错误,不执行脚本。式来表示,并给函数起一个名字。以后再用到这些代码时,就可以直接使用函数名来调用这些代码了。
函数的定义格式如下:
function 函数名() {
命令列表
}
函数的命名规则与变量的命令规则相同。调用函数的时候直接使用函数名。
编写函数func,该函数的任务是现实当前的日历、日期和工作路径,并显示一行字符串。要执行这个函数,只需要调用函数名func即可。
#! /bin/bash
function func()
{
cal
date
ped # 将pwd写成ped,出错后期调试用
echo "this is function!"
}
# 调用两次函数
func
func
注意:脚本本身没有问题,但有可能会报Syntax error: "(" unexpected
,可排查下版本等问题,或将执行命令改为:
bash -x 脚本名
编写万Shell脚本后,可以在Shell中进行脚本的调试。调试程序对于程序员来说是一项必不可少的工作。调式脚本的命令是bash
,命令格式如下:
bash [选项] 脚本名
常用选项有:
-x
:在执行脚本是现实脚本的内容,以方便用户追踪调试程序-n
:只检查脚本的语法错误,不执行脚本。