此篇博客主要讲述Shell中的参数解析和条件判断
与在Linux中写其他程序一样,Shell脚本在执行时也是可以在命令行输入参数的,Shell中使用参数可以提高脚本运行的目的性和效率。参数解析的引入可以为Shell中的过程控制提供有效的条件判断方法。
先看看下面的例子:
#!/bin/bash
##FileName: argu.sh
echo "The total count of the argument is: "$#
echo -e "\nAll of the arguments are: "
for i in "$*"; do
echo $i
done
echo -e "\nThe sequence of the arguments is: "
for i in "$@"; do
echo $i
done
以上就是一个参数解析的例子,上面的Shell代码实现了计算参数个数、打印参数值、打印参数列表的功能,此Shell脚本的执行结果如下:
[shallwing@centos ~]$ bash argu.sh Baidu Alibaba Tencent Google Apple Microsoft
The total count of the argument is: 6
All of the arguments are:
Baidu Alibaba Tencent Google Apple Microsoft
The sequence of the arguments is:
Baidu
Alibaba
Tencent
Google
Apple
Microsoft
在上面的脚本源码里:
$#
表示参数的个数,上述例子里面,本人在命令行输入了6个参数,所以输出的$#
的值是6;$i
表示的是第i个参数的值,例如在上面的例子里,$3
的值就是"Tencent";$*
表示的所有参数连接构成一个字符串之后的值,上面的例子里面,$*
的值是“Baidu Alibaba Tencent Google Apple Microsoft”;$@
表示所有参数构成的一个参数序列,$*
与$@
的区别正在于此,上面的例子里,$@
的值便是序列“Baidu” “Alibaba” “Tencent” “Google” “Apple” “Microsoft” 。
脚本中的for是一个循环控制的语句,这个用法本人会在之后的博客中再次讲述,一般来说,用于for循环条件控制的是序列,而非单个的值,所以在实际脚本编写当中都是使用$@
做条件控制,而非$*
。
Shell中参数解析的部分容易理解和上手。
Shell中的条件判断的关键字是if-else或case,与C语言中的if-else和case相似,但是其中多了很多的符号,并且使用时必须遵循特定的格式,Shell中的常用关系运算符如下:
字符串比较运算符 | 含义 |
---|---|
= | 等于 |
!= | 不等于 |
-z | 判断字符串是否长度为0(is zero) |
-n | 判断字符串长度是否不为0(is not zero) |
$ | 判断字符串是否为空($[variable]) |
数字关系运算符 | 含义 |
---|---|
-gt | 大于(greater than) |
-lt | 小于(littler than) |
-eq | 等于(equal) |
-ne | 不等于(not equal ) |
-ge | 大于或等于(greater or equal) |
-le | 小于或等于(littler or equal) |
下面便是shell中条件判断的两个例子:
#!/bin/bash
##Filename=gussWhoami.sh
answer=`whoami`
if [ -z $1 ] ; then
echo "gussWhoami.sh: Invalid argument -- 'empty'"
echo "please input a name: 'gussWhoami.sh [NameString]'"
exit
fi
if [ $answer = $1 ] ; then
echo "You're right, my name is $1"
else
echo "Sorry, I'm not $1"
fi
#end
#!/bin/bash
##FileName=numCompare.sh
null=
if [[ $1 = $null || $2 = $null ]] ; then
echo "numCompare.sh: Invalid argument"
echo "Please input two numbers."
exit
fi
echo "num1=$1, num2=$2"
if [ $1 -eq $2 ] ; then
echo "Num1 is equal to Num2."
fi
if [ $1 -lt $2 ] ; then
echo "Num1 is litter than Num2."
fi
if [ $1 -gt $2 ] ; then
echo "Num1 is greater than Num2."
fi
#end
第一个例子是用if-else完成字符串的比较,这个脚本里面需要用户自己输入一个姓名对应的字符串,然后再在系统中使用whoami的命令查看当前的用户的名字,完成输入的名字同当前用户名字的比较,也就是一个“猜猜我是谁”的简单shell游戏,其中的exit命令表示退出当前的shell脚本执行的进程。
第二个例子就是用if-else完成数字的比较,脚本需要用户输入两个数字,然后来判断这两个数字谁大谁小,之后再打印出比较的结果。
以上的两个例子中明确的给出了if-else条件判断的写法:if后的条件语句要用中括号括起来,至于是使用双中括号还是单中括号的问题,在于变量的类型是否已经确定,但是用双中括号括起来是绝对正确的,实际情形中也是这样操作的;条件判断语句之后,紧跟着一条套用的语句 “;then”,这也是必不可少的,then之后就是一条条的命令语句,当命令语句写完之后,一定要加上一个fi(if的倒序写法),表示条件判断流程的结束。
case型的条件判断语句同C语言中的使用的情形是一样的,主要用于多分支的条件判断,以下便是case型条件判断的一个例子:
#!/bin/bash
##FileName=score.sh
judge="The range of this score is in grade "
echo "Please input a student's score:"
read score
if [[ $score -lt 0 || $score -gt 100 ]] ; then
echo "The student score must in range [0,100]."
echo "Fail to judge the score grade."
exit
fi
score=`expr $score / 10`
case $score in
6) echo ${judge}"D." ;;
7) echo ${judge}"C." ;;
8) echo ${judge}"B." ;;
9) echo ${judge}"A." ;;
10) echo ${judge}"AA." ;;
*) echo ${judge}"E." ;;
esac
#end
上面的case例子也是C语言中case的经典举例,就是输入一个学生的考试成绩,来判断这个成绩的等级,上面例子中的read同C语言中的scanf()类似,起着阻塞进程,等待用户输入数据的功能。以上的书写格式也是shell中case使用的基本格式。case语句的开头是case [value] in
,然后再是[mode]) [command];;
一条条的不同条件处理的语句,其中的mode就是不同的条件模式,command便是不同条件模式下的处理命令,可以是一条或多条,*)表示任意条件模式,每一个条件模式完成之后,需要输入";;"表示一个mode已经完成。当一个case完成之后,需要输入关键字esac(case的倒序写法),来表示case条件判断结束。
在第一篇Linux Shell笔记的博客中,本人已经通过shell完成了网卡IP地址的获取,现在我们可以对之前的程序做一下功能的扩展与优化:
怎样才能知道用户键入的网卡是否存在呢,一般来说,通过“ip a”的命令便可以显示所有本机上所有使能网卡的名称,因此,我们可以利用“ip a”与管道和grep命令相结合的方式,来完成参数的解析。之后,再利用if条件判断,来指定不同的网卡获取哪些信息。
实现的代码如下:
#!/bin/bash
Usage="$1: Network interface '$1' does not exist!"
inter=$1
if [[ ${#inter} -lt 2 ]] ; then
echo $Usage
exit
fi
CHECK=`netstat -i | grep "$1 "`
if [[ $CHECK = $NULL ]] ; then
echo -e $1": Network interface '$1' does not exist!"
exit
fi
ip="ip a sh $1"
if [[ $1 = "lo" ]] ; then
ip_addr=`$ip | grep "scope host lo"`
ip_addr=${ip_addr%"scope host lo"*}
echo -e "$1:\n"${ip_addr}
ip_addr=`$ip | grep "inet6"`
ip_addr=${ip_addr%%"scope host"*}
echo " inet6"${ip_addr##*"inet6"}
ip_addr=`$ip | grep "brd"`
ip_addr=${ip_addr##*"brd"}
echo " broadcast"${ip_addr}
else
if [[ `$ip | grep "inet"` != $NULL ]] ; then
ip_addr=`$ip | grep "brd" | grep "inet"`
ip_addr=${ip_addr%%"scope"*}
echo -e "$1:\n"${ip_addr}
ip_addr=`$ip | grep "inet6"`
ip_addr=${ip_addr%%"scope"*}
echo " inet6"${ip_addr##*"inet6"}
else
ip_addr=`$ip | grep "link"`
echo -e "$1:\n"${ip_addr}
fi
fi
#end
以上例子中的参数解析的逻辑是:若没有在“ip a”命令执行的结果里查找到对应的网卡或用户键入参数为空,则直接退出并打印错误信息。
之后获取IP等网卡配置的信息,和之前的方法一样,通过观察不同网卡之前配置信息书写格式的共性,来指定一套统一的信息截取方法。比如,loopback网卡只有广播的信息,而无MAC地址等信息,所有就单独拿出来作为一个特殊的条件判断模式,Debian系列中eth0网卡的信息也是如此。
第二期shell笔记经验分享基本结束,下次将讲述的是shell中的函数、数组与循环。