第十一章 Shell编程

第十一章 Shell编程

​ 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

1.编写Shell脚本

​ Shell脚本也成为Shell程序,它是由一系列Shell命令构成地文本文件。Shell程序可以是一些列Shell命令地组合,也可以利用各种程序控制结构(分支结构、循环结构等)编写复杂的脚本程序。总之,Shell脚本是用户根据需要编写的用于解决一系列问题的程序。

1.1.建立Shell脚本

​ 建立Shell脚本的方法有很多,由于Shell脚本是文本文件,所以可以使用文本编辑器建立和编辑脚本,常用的文本编辑器有vi、emacs、Gedit等。

​ 首先,通过文本编辑器完成一个简单的Shell脚本编写。打开文本编辑器,输入下列程序:

#! /bin/bash
# this is my first shell script
echo "Hello World!!1"
date

​ 保存该文件,将文件命名为helloworld,这样就建立了以恶名为helloworld的脚本文件。纵览该文件可以发现,Shell脚本的编写方法非常简单,各行的含义如下:

  • 第一行知名Shell脚本使用哪个Shell解释执行,在Ubuntu中,默认的Shell是bash,所以在以后的所有Shell脚本的编写中,第一行都要按照此格式编写,以指明Shell使用的版本;
  • 第二行是程序的注释,添加注释的方法是在行首添加#符号,代表后面的字符都是注释;
  • 第三行的任务是利用echo命令输出一行字符:‘helloworld’;
  • 第四行是利用date命令显示系统的当前日期和时间。

1.2.执行Shell脚本

1.赋予脚本文件可执行的权限,改变脚本文件的属性:

chmod 755 文件名
或
chmod a+x 文件名

2.文件具有可执行权限后就可以执行了,在文件所在的目录输入:

./文件名
或
bash 文件名
或
. 文件名
或
source 文件名

2.交互式Shell脚本

​ 在高级语言 程序设计中,编写交互式程序是提高程序可读性、实用性的一个时分常用的手段。交互式程序允许用户在程序的执行过程中输入数据,程序会根据用户输入的数据进行响应的操作。

​ 交互式Shell脚本的执行也与上述情况类似,因此,学习交互式Shell脚本的编写时学习Shell脚本设计必不可少的一环。Shell脚本中需要使用read命令读取用户输入的值(字符串),记录在变量中。

read命令的格式如下:

read [-p "字符串"] 变量名

​ 例如,编写一个交互式Shell脚本,要求用户输入一行字符串,然后将此字符串显示出来。脚本如下所示:

第十一章 Shell编程_第1张图片

​ 上图脚本利用name变量记住用户交互式输入的用户名,再利用echo命令输出name变量的值。后中断交互式脚本,在执行脚本,用户输入名字后并可显示出来。

3.逻辑判断表达式

test命令可以对表达式的执行结果进行判断。表达式可以包括文件、整数、字符串。但是test命令执行结束时并不显示任何判断结果,而是用返回值来表示判断的结果。返回值为0时,表示判断结果为真;返回值为1时,表示判断结果为假。test命令的判断结果主要用于程序控制结构(如if语句)中的条件判断。

test命令格式如下:

test 表达式 或者 [表达式]

​ 使用test命令时应注意:

  • 在"[表达式]"中,要注意"[""]"两侧都要有空格;
  • 表达式中的运算符两侧也应保留空格;
  • 如果运算符时Shell的元字符,如*、&、|、<、>等,必须用转义符\使其失去特殊含义,不被Shell解释执行;
  • 返回值为0时,表示判断结果为真;返回值为1时,表示判断结果为假。

test命令的常用表达式有文件判断、整数判断、字符串判断、逻辑判断。

3.1.文件判断

test命令主要用于检验一个文件的类型、属性、比较两个值。文件判断操作符可以用来进行文件的比较,如下表所示:

操作符号 含义 操作符号 含义
-d 确定文件是否为目录 -e 确定文件是否存在
-f 确定文件是否为普通文件 -w 确定是否对文件设置了写权限
-r 确定是否对文件设置了读权限 -x 确定是否对文件设置了执行权限
-s 确定文件名长度是否大于0
test -f /etc/passwd							# 判断文件是否为一个普通文件
echo $?										# 输出结果
test -r /etc/passwd							# 判断文件是否具有读权限
echo $?

3.2.整数判断

​ 整数判断操作符可以用来比较两个整数,如下标所示:

操作符号 含义 操作符号 含义
-eq 比较两个整数是否相等 -ne 比较两个整数是否不等
-ge 比较两个整数是否大于或等于另一个整数 -gt 比较一个整数是否大于另一个整数
-le 比较一个整数是否小于或等于另一个整数 -lt 比较一个整数是否小于另一个整数

​ 例如,利用整数判断操作符对整数进行比较。通过test命令判断两个整数30和90是否相等,结果为假,返回1.再判断30和90是否不等,结果为真,返回值为0.

第十一章 Shell编程_第2张图片

3.3.字符串判断

​ 字符串判断操作符可以用来比较两个字符串表达式,如下表所示:

操作符号 含义 操作符号 含义
= 比较两个字符串是否相等 -z 判断字符串长度是否等于0
!= 比较两个字符串是否不相等 -n 判断字符串长度是否大于0

​ 定义一个name变量,并赋值为user。用test命令判断name变量的值是否等于user,结果为真,返回值为0。注意,在test命令中,等号两边都要保留空格。

第十一章 Shell编程_第3张图片

​ 以下示例判断字符串是否含有空格:给name变量赋予一个带空格的字符串user zhang,赋值时应该把这个字符串整体用双引号括起来,即引号内的所有字符串作为一个整体来处理。用test命令进行判断的时候也应该注意这个问题,对name变量的取值也要加双引号,即"$name",然后再进行判断。

第十一章 Shell编程_第4张图片

3.4.逻辑判断

test可以对带有逻辑运算符的表达式进行判断。逻辑判断就是要测试带有逻辑运算符的表达式的结果(为真或者假)。常用的逻辑运算符有与(and)、或(or)、非(not),在shell中分别用-a、-o!表示。

​ 逻辑判断一般和文件判断、字符串判断、整数判断结合使用。以下是进行逻辑判断的test命令示例:

  • 判断/etc/passwd文件是否为具有读属性的普通文件:

第十一章 Shell编程_第5张图片

​ 以上表达式可以分为3个部分来理解:

  1. -f /etc/passwd是判断该文件是不是一个普通文件;
  2. -r /etc/passwd是判断该文件是不是具有只读的属性;
  3. -a是1,2两部分的结合,是“与”判断,是“并且”的意思,即1,2是不是同时成立。
  • 判断输入的整数判断表达式的逻辑结果:输入3个整数4、25、90,用3个变量分别记住这些整数。test命令中的表达式的含义是判断( $a=4 or $b>20 \) and ($c<=100) 是否为真,再用$?待会返回值,结果判断为真,返回0。

第十一章 Shell编程_第6张图片

4.分支结构

​ Shell脚本和其他语言类似,在分支结构中也提供了分支结构和循环结构两大类。分支结构包括if语句和case语句。

4.1.if语句

​ 在Shell中任何命令都有返回状态,因此,都可以作为if语句的条件使用。此外,在Shell中还提供了true命令,false命令以及:命令。他们只返回一个特定的值::true命令返回值为0,false命令返回值为非0。

if语句的语法格式如下:

if [条件命令]
then
	命令1
	命令2
	...
fi
或者
if [条件命令]; then
	命令1
	命令2
	...
fi

说明:

  1. 首先执行条件命令,如果条件命令的返回值为0,则执行thenfi之间的命令;如果返回值为非0,则不执行这些命令;
  2. 条件命令通常是一个test表达式命令,也可以是其他命令或命令列表。如果是命令列表,则Shell将一次执行各个命令,并把最后一个命令的返回值作为条件命令的结果;
  3. 条件命令的两边有空格,即使用空格把条件命令个"[" "]"分隔开。if语句的结束用fi表示。

创建脚本文件,内容如下:

#! /bin/bash
read -p "input your name:" name
if [ $name = "user" ]; then
        echo "hello $name"
fi

修改文件权限:

chmod 755 文件名

执行文件:

sh 文件名

第十一章 Shell编程_第7张图片

4.2.if-else语句

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 文件名

第十一章 Shell编程_第8张图片

4.3.if-elif-else语句

​ 利用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 文件名

第十一章 Shell编程_第9张图片

4.4.嵌套if语句

​ 嵌套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 文件名

第十一章 Shell编程_第10张图片

4.5.case语句

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编程_第11张图片

5.循环结构

​ 循环结构用来控制某个处理过程的重复执行。Shell提供了3中循环结构:for循环、while循环、until循环。

5.1.for循环

​ 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 文件名

5.3.while循环

​ 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 文件名

第十一章 Shell编程_第12张图片

​ **案例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系统的问题,脚本本身没有问题。

5.4.until循环

​ 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 文件名	

第十一章 Shell编程_第13张图片

5.5.break和continue

​ 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

第十一章 Shell编程_第14张图片

示例: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编程_第15张图片

6.函数

​ 在Shell变成中,用户可以编制自己的函数,函数可以简化程序,使程序的结构更加清晰,而且可以避免程序中重复编写某些代码的工作。因此,当某一段代码会在程序中被经常使用或重复执行时,可以把这些代码以函数的形式来表示,并给函数起一个名字。以后再用到这些代码时,就可以直接使用函数名来调用这些代码了。

​ 函数的定义格式如下:

function 函数名() {
	命令列表
}

​ 函数的命名规则与变量的命令规则相同。调用函数的时候直接使用函数名。

​ 编写函数func,该函数的任务是现实当前的日历、日期和工作路径,并显示一行字符串。要执行这个函数,只需要调用函数名func即可。

#! /bin/bash
function func()
{
        cal
        date
        ped    # 将pwd写成ped,出错后期调试用
        echo "this is function!"
}

# 调用两次函数
func
func

注意:脚本本身没有问题,但有可能会报Syntax error: "(" unexpected,可排查下版本等问题,或将执行命令改为:

bash -x 脚本名

7.调试脚本

​ 编写万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 脚本名

7.调试脚本

​ 编写万Shell脚本后,可以在Shell中进行脚本的调试。调试程序对于程序员来说是一项必不可少的工作。调式脚本的命令是bash,命令格式如下:

bash [选项] 脚本名

​ 常用选项有:

  • -x:在执行脚本是现实脚本的内容,以方便用户追踪调试程序
  • -n:只检查脚本的语法错误,不执行脚本。

第十一章 Shell编程_第16张图片

你可能感兴趣的:(Linux,linux)