Shell流程控制

本章主要介绍流程控制部分 主要是记录shell中ifelseelifexitcase,test等用法,还有逻辑操作符:与或非等。

退出状态

Unix系统中,每当程序执行完之后,它都会给系统返回一个退出状态,该状态是个数值,如果是执行成功则返回0,如果执行失败则返回非0即可。

if

if的命令格式如下,其中condition为执行条件,并检测其推出状态,如果退出状态为0,则执行thenfi之间的命令,否则跳过。如果在条件判断中,并不想展现执行语句输出的内容,则可以把执行结果重定向到回收站中/dev/null

if condition  then      command    commandfi  

$?变量

Shell会把所执行的命令的退出状态自动保存到$?变量中,以上所述执行成功总是返回0,如果执行失败返回的是哪个非0的数字,这个看系统实现了。举个栗子:

cp a1.txt a31.txt  # 输出 0,表示执行成功echo $?rm c  # 输出 1,表示执行失败 (rm: c: is a directory)echo $?  

test命令

test命令,看这名字就知道是测试,它一般用在if语句中,需要判断的语句放在test命令后即可,如test expression,如果expression为真,它返回退出状态为0,否则不为0。test会把气候的所有信息都与参数对待,所以操作数和操作符之间得至少有一个空格。举栗几个常用的判断形式。

# 判断两个字符串是否相等test \"$testStr\" = \"123\"# 判断两个字符串不相等test \"$testStr\" != \"123\"# 判断字符串不为空test \"$testStr\"# 以上例子中变量都是用双引号包裹起来的,这样会保留变量中的空格等# 回想一下[上一篇shell编程基础](https://schoeu.com/shellscript-basics/)中的变量替换# 如果我们想去掉首位多余的空格,可以不给变量加引号。# ------------------------------# 以下例子中,因为用到了-n,-z等操作符# 操作符后面必须得跟参数,所以操作符后面跟变量的用引号包裹起来# 判断字符串不为空test -n \"$testStr\"# 判断字符串为空test -z \"$testStr\"  

test的简写形式

在各种语言中,判断是使用最频繁的表达形式了,每次写test感觉有点不爽,所以shell有一种该命令的简写形式[ expression ],\b注意方括号两侧的空格,这是它特定的执行格式,所以上面的命令可以写为:

[ \"$testStr\" = \"123\" ]

下面整理一些判断常用的操作符。

整数操作符

常用的整数操作符如下,并举例说明变量在判断条件中的情况:

# 等于\"$num1\" -eq 123# 大于\"$num1\" -gt \"$num2\"# 大于或等于\"$num1\" -ge \"$num2\"# 小于\"$num1\" -lt 123# 小于或等于\"$num1\" -le 123# 不等于\"$num1\" -ne 123num1=00100  num2=\"  10\"# 输出:\"  10\" equal 10if [ $num2 -eq 10 ]  then      echo '\"  10\" equal 10'else      echo '\"  10\" not equal 10'fi# 输出:$num2 equal 10if [ \"$num2\" -eq 10 ]  then      echo '$num2 equal 10'else      echo '$num2 not equal 10'fi# 输出:00100 equal 100if [ $num1 -eq 100 ]  then      echo '00100 equal 100'else      echo '00100 not equal 100'fi  

文件操作符

shell在判断中的一些文件操作符如下:

# -d: 判断file为目录if [ -d a ]  then      echo \"a is a dir.\"else      echo \"a is not a dir.\"fi# -e: 判断文件是否存在if [ -e a1.txt ]  then      echo \"a1.txt is exist.\"else      echo \"a1.txt is not exist.\"fi# -f: 判断是否是普通文件if [ -f a1.txt ]  then      echo \"a1.txt is a file.\"else      echo \"a1.txt is not a file.\"fi# -r: 判断文件为可读if [ -r a1.txt ]  then      echo \"a1.txt is readable..\"else      echo \"a1.txt is not readable.\"fi# -w: 判断文件是否可写if [ -w a1.txt ]  then      echo \"a1.txt is writable.\"else      echo \"a1.txt is not writable..\"fi# -x: 判断文件是否可执行if [ -x a1.txt ]  then      echo \"a1.txt is executable.\"else      echo \"a1.txt is not executable.\"fi# -s: 判断文件是否至少包含一个字节的信息,长度不为0if [ -s a1.txt ]  then      echo \"a1.txt is not empty.\"else      echo \"a1.txt is empty.\"fi# -L: 判断是否为链接文件if [ -L a1.txt ]  then      echo \"a1.txt is symbolic link.\"else      echo \"a1.txt is not symbolic link.\"fi  

非操作符

! expression,非操作符可以放在其他表达式前,对该表达式求非。它是一个操作符,其后表达式它认为是自己的参数,所以中间以空格隔开,如:[ ! -d a1.txt ],表示a1.txt不是一个文件夹时退出状态返回0,条件成立。

与操作符

expression -a expression,与其他语言一样,如果两个表达式均为真,则返回真。比如要判断一个文件即存在又可写,则可写为[ -e a.txt -a -w a.txt ]

或操作符

语法为:expression -o expression,条件中有一个为真则返回真。如判断一个文件是不是可读或者可写:[ -w a.txt -o -r a.txt ]

条件优先级

当条件较复杂的时候,肯定会有优先级的问题,在shell中和其他语言一样,也是适用小括号来解决的,不过有一点差别,小括号在shell中有特殊意义,所以要改变条件优先级的使用,要么把小括号用引号包裹起来,要么使用\\转义符来去掉括号的特殊意义。举个例子:

# 使用\\来取消小括号的特殊含义if [ \\( -e a.txt \\) -a \\( -n a.txt \\) ]  then      echo 'The file exist and not empty.'fi# 我们也可以使用引号来让shell忽略特殊符号if [ '(' -e a.txt ')' -a '(' -n a.txt ')' ]  then      echo 'The file exist and not empty.'fi  

Tips:逻辑与,逻辑或操作符优先级低于整数操作符和文件操作符,so,以上的例子有小括号和不加小括号执行的结果是没有差别的。

exit命令

该命令用来终止shell程序的执行,语法为:exit n,n为返回的退出状态,如果不指定,就默认使用之前最后所执行命令的退出状态。

# 当a.txt不存在是直接退出shell执行if [ ! -e a.txt ]  then      exit 1;else      expressionfi  

elif

前面说了ifelse,那按套路来讲是不是得有其他语言中的elseif,shell是有,只是和其他语言有那么一小点点差别,它叫elif,用法和if一样,如:

if [ expression ]  then      expressionelif [ expression ]  then      otherexpressionelse      lastexpressionfi  

case

如果有一种场景是我们需要和很多个值来对比,然后执行一些命令,则case更合适一些,如果使用elif可能会写出很长的代码。shell中的case相当于其他语言中的switch-case语句,与哪个case的值匹配,它就会执行该条件下的语句。如果没有匹配到任意一项,则不会执行case中的任何语句。语法有点特殊,是这个样子的:

case somevalue in  # 符合哪个pattern,shell则会执行从条件后到双分号之间的所有语句。    pat1) command          command;;    pat2) command          ...          command;;esac  

按我们的理解,case语法中的;;就相当于js中switch-case中的break吧,那匹配不到任何一项,我们能给他设置default么,shell中是这样做的,它使用了前面提到的模式匹配字符,比如使用通配符*,它代表匹配任何结果,综合来看个输入字符来判断输出的栗子:

number=$1  if [ ! $# -eq 1 ]  then      echo 'one number'    exit 1ficase $number in      1)  echo 'one'        echo 'this is first number';;    2)  echo 'two';;    *)  echo 'it is default value';;     esac# -----------------------------------# 比如要匹配字符或数字,可以这样case $value in      [0-9])  echo 'number';;    [a-zA-Z])  echo 'char';;    *)  echo 'it is default value';;     esac# -----------------------------------# 可能还有更复杂的case,如我们想在一个case中匹配多种情况# 那么可以使用|,在case中它表示逻辑或# 继续例子case $value in      1[1-9])  echo '匹配到了10 < x < 20的数值';;    [a-z] | [A-Z])  echo 'char';;    *)  echo 'it is default value';;     esac  

调试shell

在执行shell脚本的时候,加-x参数,shell就会吧所执行到的所有命令和值都打出来,方便开发者追踪执行路径,找到不符合预期的地方,达到调试的效果。

空命令

空命令顾名思义,是神马都不做,那既然神马都不做,要它何用??这就得说shell中的一些看似奇葩的规定了,如在if-else块中,shell要求then之后必须有一条命令,有else的时候else中也不能为空,so...

if [ expression ]  then      :else      echo 'else block'fi  

有童鞋会说,那把条件取反,然后把要执行的语句放到if中不就可以了么,当然也行,只是方便使用而已。

&&与||

在shell中,这两兄弟和js中的与和或运算符执行规则很相似。

  • &&:语法expression1 && expression2当expression1退出状态为0,也就是返回正常(对shell来说为真)时则执行expression2,如果返回退出状态不为0(对shell来说为假)时则跳过expression2不执行。可以作为if的简写形式。效果等同于:
if [ expression1 ]  then      expression2fi  
  • ||:语法与上面一样,区别在于当expression1退出状态不为0时,才执行expression2。 效果等同于:
if [ ! expression1 ]  then      expression2fi  

当然这两兄弟也能混合到if-else中,做一些复杂的逻辑控制。

总结

本文学习了shell中基本的语言条件判断与跳出。语言都是想通的,我们可以从一门熟悉的语言中相似的特性来理解新语言的语法与思想,这样学习会更快写,且不容易忘记。

comments powered by Disqus