shell/Shell 流程控制_函数编程.md

24 KiB
Raw Permalink Blame History

Shell流程控制-函数

一、Shell 编程之条件结构

0、走在测试之前--认识各种括号

[]、()、{}、(())、$(())、$[]、$()、${}

1.单括号[] ()

[]	test命令的另一种写法	 
()	重新开一个子shell然后执行里面的最后一个命令可以不用分号
{}	在当前 shell 执行,里面的最后一个命令要用分号,第一个命令和左括号之间必须有一个空格

()和{}里的某个命令的重定向只影响该命令, 而括号外的重定向则影响到括号里的所有命令。

()测试示例

# 测试数据定义
[root@qfedu.com ~]# var=test

# 子shell中执行命令
[root@qfedu.com ~]# echo $var
test
[root@qfedu.com ~]# (var=notest;echo $var)
notest
[root@qfedu.com ~]# echo $var
test
# 里的执行完毕后没有改变当前shell变量的值说明在子shell中执行的

{}测试示例

[root@qfedu.com ~]# {var=notest;echo $var}
{var=notest: command not found
test}
[root@qfedu.com ~]# {var=notest;echo $var;}
-su: syntax error near unexpected token `}'
[root@qfedu.com ~]# { var=notest;echo $var;}
notest
[root@qfedu.com ~]# echo $var
notest
# {}修改了变量的值。表明在当前shell中运行的

适用场景
# { ls;pwd;}>a.txt  # 对比一下下面的输出有何不同
# ls;pwd>a.txt 

[]测试示例-见test命令详解

2.双括号(())

(()) 适合数值比较、运算 C语言形式

[root@wing tmp]# a=8
[root@wing tmp]# b=9
[root@wing tmp]# ((c=a+b))
[root@wing tmp]# echo $c
17

[root@wing tmp]# ((a>b))
[root@wing tmp]# echo $?
1
[root@wing tmp]# ((a<b))
[root@wing tmp]# echo $?
0

适合条件测试,支持正则

3.$修饰的括号

$(())、$[]、$()、${}

$(())、$[]	用途一致,用来作整数运算
$()  		 命令替换,等同反引号``Shell会先执行括号内的cmd然后将结果输出
${}     	 Shell中变量的原形用于限定变量名称的范围并且支持通配符

# $(( ))还可以作不同进制(如二进制、八进位、十六进制)运算,只是输出结果皆为十进制而已。

# 16进位转十进制
[root@qfedu.com ~]# echo $((16#2a))  
42
# 2进制转十进制
[root@server test]# echo $((2#11))
3

1、Shell 条件测试方法

方法 描述
test 测试表达式 利用test命令进行条件测试表达式test命令与测试表达式之间至少有一个空格
[ 测试表达式 ] 通过[ ]中括号进行条件测试表达式,[]中括号边界与测试表达式之间至少有一个空格
测试表达式 通过 双中括号进行条件测试表达式, 双中括号与测试表达式之间至少有一个空格
((测试表达式)) 通过(( ))双小括号进行条件测试表达式,( ))双小括号两端不需要空格,常用于整数对比

2、test测试

1.文件操作符

语法

# test 操作符 文件或目录

	或者

# [ 操作符 文件或目录 ]
操作符 描述
-d 文件 文件存在且为目录则为真
-f 文件 文件存在且为普通文件则为真
-e 文件 文件存在则为真,不辩别是目录还是文件
-s 文件 文件存在且文件大小不为0则为真
-r 文件 文件存在且可读则为真,与执行脚本的用户权限也有关
-w 文件 文件存在且可写则为真,与执行脚本的用户权限也有关
-x 文件 文件存在且可执行则为真,与执行脚本的用户权限也有关
-L 文件 文件存在且为链接文件则为真
f1 -nt f2 文件f1比文件f2新则为真根据文件的修改时间计算
f1 -ot f2 文件f1比文件f2旧则为真根据文件的修改时间计算

示例

[root@qfedu.com  ~]# test -d /home
[root@qfedu.com  ~]# echo $?
0
[root@qfedu.com  ~]# test -d /home11111
[root@qfedu.com  ~]# echo $?
1
[root@qfedu.com  ~]# [ -d /home ]
[root@qfedu.com  ~]# [ ! -d /ccc ]  && mkdir /ccc
[root@qfedu.com  ~]# [ -d /ccc ]  || mkdir /ccc

2.字符串操作符

参数 示例 功能
-z s1 如果字符串s1的长度为0则测试条件为真
-n s1 如果字符串s1的长度大于0则测试条件为真
s1 如果字符串s1不是空字符串则测试条件为真
=或== s1=s2 如果s1等于s2则测试条件为真,“=”前后应有空格
!= s1!=s2 如果s1不等于s2则测试条件为真
< s1<s2 如果按字典顺序s1在s2之前则测试条件为真
> s1>s2 如果按自定顺序s1在s2之后则测试条件为真

示例

# 字符串比较必须使用双引号
[root@qfedu.com  ~]# [ "$USER" = "root" ];echo $?
0
[root@qfedu.com  ~]# [ "$USER" == "root" ];echo $?
0

[root@qfedu.com  ~]# BBB=""
[root@qfedu.com  ~]# echo ${#BBB}
0
[root@qfedu.com  ~]# [ -z "$BBB" ]	            # 字符长度是为0
[root@qfedu.com  ~]# echo $?
0
[root@qfedu.com  ~]# [ -n "$BBB" ]	            # 字符长度不为0
[root@qfedu.com  ~]# echo $?
1

[root@qfedu.com  ~]# var1=111
[root@qfedu.com  ~]# var2=
[root@qfedu.com  ~]# 							# var3变量没有定义
[root@qfedu.com  ~]# echo ${#var1}
3
[root@qfedu.com  ~]# echo ${#var2}
0
[root@qfedu.com  ~]# echo ${#var3}
0
[root@qfedu.com  ~]# [ -z "$var1" ];echo $?
1
[root@qfedu.com  ~]# [ -z "$var2" ];echo $?
0
[root@qfedu.com  ~]# [ -z "$var3" ];echo $?
0
[root@qfedu.com  ~]# [ -n "$var1" ];echo $?
0
[root@qfedu.com  ~]# [ -n "$var2" ];echo $?
1
[root@qfedu.com  ~]# [ -n "$var3" ];echo $?
1

3.整数操作符

在[]和test中使用 和(( ))中使用 说明
-eq ==或= 等于全拼为equal
-ne != 不等于全拼为not equal
-gt > 大于全拼为greater than
-ge >= 大于等于全拼为greater or equal
-lt < 小于全拼为less than
-le <= 小于等于全拼为less or equal

1、判断变量是不是数字

[root@qfedu.com  ~]# num10=123
[root@qfedu.com  ~]# num20=ssss1114ss
[root@qfedu.com  ~]# [[ "$num10" =~ ^[0-9]+$ ]];echo $?
0
[root@qfedu.com  ~]# [[ "$num20" =~ ^[0-9]+$ ]];echo $?
1

2、数值比较 [ 整数1 操作符 整数2 ]

[root@qfedu.com  ~]# disk_use=$(df -P |grep '/$' |awk '{print $5}' |awk -F% '{print $1}')
[root@qfedu.com  ~]# [ $disk_use -gt 90 ] && echo "war......"
[root@qfedu.com  ~]# [ $disk_use -gt 60 ] && echo "war......"
war......

[root@qfedu.com  ~]# id -u
0
[root@qfedu.com  ~]# [ $(id -u) -eq 0 ] && echo "当前是超级用户"
当前是超级用户
[alice@qfedu.com  ~]$ [ $UID -eq 0 ] && echo "当前是超级用户" || echo "you不是超级用户"
you不是超级用户

3、C语言风格的数值比较

[root@qfedu.com  ~]# ((1<2));echo $? 
0
[root@qfedu.com  ~]# ((1==2));echo $? 
1
[root@qfedu.com  ~]# ((1>2));echo $?
1
[root@qfedu.com  ~]# ((1>=2));echo $?
1
[root@qfedu.com  ~]# ((1<=2));echo $?
0
[root@qfedu.com  ~]# ((1!=2));echo $?
0
[root@qfedu.com  ~]# ((`id -u`>0));echo $?
1
[root@qfedu.com  ~]# (($UID==0));echo $?
0

4.逻辑操作符

在[]和test中使用 和(( ))中使用 说明
-a && and两端都为真则结果为真
-o || or两端有一个为真则结果为真
! ! not两端相反则结果为真
[root@qfedu.com  ~]# [ 1 -lt 2 -a 5 -gt 10 ];echo $?
1


1 0
[root@qfedu.com  ~]# [ 1 -lt 2  -o 5 -gt 10 ];echo $?
0

[root@qfedu.com  ~]# [[ 1 -lt 2 && 5 -gt 10 ]];echo $? 
1
[root@qfedu.com  ~]# [[ 1 -lt 2 || 5 -gt 10 ]];echo $? 
0

5、测试表达式区别总结

测试表达式符号 test [ ] (( ))
边界是否需要空格 需要 需要 需要 不需要
逻辑操作符 !、-a、 -o !、-a、 -o !、&&、|| !、&&、||
整数比较操作符 -eq、-ne、-lt、-gt、-ge、-le -eq、-ne、-lt、-gt、-ge、-le -eq、-ne、-lt、-gt、-ge、-le或=、!=、<、>、>=、<= =、!=、<、>、>=、<=
字符串比较操作符 =、==、!= =、==、!= =、==、!= =、==、!=
是否支持正则 不支持 不支持 支持 不支持

变量为空或未定义长度都为0

[root@qfedu.com  ~]# [ "$USER" = "root" ];echo $?
0
[root@qfedu.com  ~]# [ "$USER" = "alice" ];echo $?
1
[root@qfedu.com  ~]# [ "$USER" != "alice" ];echo $?
0

[root@qfedu.com  ~]# [ "$USER" = "root" ];echo $?
0
[root@qfedu.com  ~]# [ "$USER" =~ ^r ];echo $?
bash: [: =~: binary operator expected
2
[root@qfedu.com  ~]# [[ "$USER" =~ ^r ]];echo $?		# 使用正则
0

3、Shell 分支if语句

1、单分支 IF 条件语句

if [ 条件判断式 ];then
	条件成立时,执行的程序
fi

# if语句使用fi结尾和一般语言使用大括号结尾不同
# [条件判断式] 就是使用test命令判断,所以中括号和条件判断式之间必须有空格
# then 后面跟符号条件之后执行的程序,可以放在[]之后,用";"分割。也可以换行写入,就不需要";"了

2、双分支语句

if [ 条件判断式 ]then 
   条件成立时,执行的程序
else
   条件不成立时,执行的另一个程序
fi

3、多分支语句

if [ 条件判断式1 ]
 then
  当条件判断式1成立时执行程序1
elif [ 条件判断式2 ]
 then
  当条件判断式2成立时执行程序2
...省略更多条件....
else
  当所有条件都不成立,最后执行此程序
fi

4、Shell 分支case语句

case $变量名 in
    值1
        如果变量的值等于值1则执行指令1
        ;;
    值2
        如果变量的值等于值2则执行指令2
        ;;
    值3
        如果变量的值等于值3则执行指令3
        ;;
    *
        如果变量的值不等于以上列出的任何值则执行默认指令
esac

二、Shell 编程之循环结构

1、Shell 循环 for 语句

for循环的运作方式是将串行的元素一一取出依序放入指定的变量中然后重复执行含括的命令区域在do和done 之间),直到所有元素取尽为止。

其中,串行是一些字符串的组合,彼此用$IFS所定义的分隔符如空格符隔开这些字符串称为字段。

for 变量 in 值集合
do
   执行命令
done
  • for 每次从值集合中取一个值赋值给变量

  • do - done 将赋值后的变量带入执行的命令得到执行结果

  • 重复以上两个步骤,直到值集合中的值被一一获取赋值给变量的到所有结果,循环结束

    使用变量
    num=8
    for i in $(seq 2 $num)
    do
    	echo $i
    done
    
    for i in {2..8}
    
for((i=1;i<=10;i++))
do
	echo $i
done

i=1  	初始化i的值为1
i<=10 	比较条件
i++  	步长 每次加1

嵌套的
for((i=1;i<=10;i++))
do
	for((i=1;i<=10;i++))
	do
		echo $i
	done
	
	echo $i
done

2、Shell 循环 while 语句

1、while 循环语法

while 条件测试
do
  执行命令
done
  • while 首先进行条件测试如果传回值为0条件测试为真则进入循环执行命令区域否则不进入循环

  • 满足 while 测试条件,执行命令区域,直到 while 的测试条件不满足结束执行while循环如果条件一直满足执行无穷循环

3、Shell 循环 until 语句

while循环的条件测试是测真值until循环则是测假值。

until 条件测试
do
 执行命令
done
  • until 条件测试结果为假传回值不为0就进入循环。

  • 条件测试不满足,执行命令区域。直到 until 条件满足结束执行until 循环(如果条件一直不满足则执行无穷循环)。

#!/bin/bash
until ((i>10))       # 条件测试只要i值未超过10就进入循环
do
  let sum+=i         # sum+=i和sum=sum+i是一样的sum累加上i
  let ++i			 # i的值递增1此行是改变条件测试的命令一旦i大于10可终止循环
done 				 # 遇到done回到 until条件测试
echo $sum 			 # 直到 until 的条件满足显示sum的值
#!/bin/bash
a=1
b=1
until ((a>9))			 # 条件测试只要a值未超过9就进入循环一旦超过9就不执行until和while条件相反条件真就done结束
do
     until ((b>a)) 		 # b>a,一旦b大于a就不执行
     do
          let "c=a*b"
           echo -n "$a*$b=$c "
           let b++
     done
     let a++
     let b=1
     echo ""
done

4、Shell 循环控制

breakcontinueexit 一般用于循环结构中控制循环的走向。

break和continue只能用在循环里面

exit可以用于脚本的任何地方

命令 说明
break n n 表示跳出循环的次数,如果省略 n 表示跳出整个循环
continue n n 表示退到第n层继续循环如果省略n表示跳过本次循环进入下一次循环
exit n 退出当前的shell程序并返回 nn 也可以省略
return 用于返回一个退出值给调用的函数
shift 用于将参数列表list左移指定次数最左端的那个参数就从列表中删除其后边的参数继续进入循环

break[N]提前结束第N层循环最内层为第1层

while CONDITION1; do
      CMD1
      ...
      if CONDITION2; then
                break
      fi
      CMDn
      ...
done

break结束本次循环整个退出脚本

[root@qfedu.com  ~]#vim test.sh
#!/bin/bash
for i in {1..10}
do
        [ $i -eq 5 ] && break
        echo i=$i
        sleep 0.5
done
echo test is finished
                                       
[root@qfedu.com  ~]#chmod +x test.sh
[root@qfedu.com  ~]#./test.sh       
i=1
i=2
i=3
i=4
test is finished


[root@localhost ~]# cat break1.sh
#!/bin/bash
for((i=0;i<=5;i++))
  do
    if [ $i -eq 3 ];then
         break;
    fi
       echo $i
   done
echo "ok"
 
# 运行结果为:
[root@localhost ~]# bash break1.sh
0
1
2
ok

continue [N]提前结束第N层的本轮循环而直接进入下一轮判断最内层为第1层

while CONDITION1;do
      CMD1
      ...
      if CONDITION2; then
                continue
      fi
      CMDn
      ...
done
[root@localhost ~]# cat continue.sh
#!/bin/bash
for((i=0;i<=5;i++))
  do
    if [ $i -eq 3 ];then
         continue;
    fi
       echo $i
   done
echo "ok"
 
#运行结果为:
[root@localhost ~]# bash continue.sh
0
1
2
4
5
ok

[root@qfedu.com  ~]#vim test.sh     
#!/bin/bash
for i in {1..10}
do
        [ $i -eq 5 ] && continue
        echo i=$i
        sleep 0.5
done
echo test is finished
                                                 
[root@qfedu.com  ~]#./test.sh  
i=1
i=2
i=3
i=4
i=6
i=7
i=8
i=9
i=10
test is finished

exit 指令

[root@localhost ~]# cat exit.sh
#!/bin/bash
for((i=0;i<=5;i++))
  do
    if [ $i -eq 3 ];then
         exit
    fi
         echo $i
   done
echo "ok"
 
#运行结果为:
[root@localhost ~]# bash exit.sh
0
1
2

5、shift 指令(了解)

shift 命令用于将参数列表 list 左移指定次数,最左端的那个参数就从列表中删除,其后边的参数继续进入循环。

shift[N]:用于将参量列表 list 左移指定次数,缺省为左移一次。

参量列表 list 一旦被移动,最左端的那个参数就从列表中删 除。while 循环遍历位置参量列表时,常用到 shift

实例

[root@qfedu.com  ~]# vim demo.sh
#!/bin/bash
while [ $# -gt 0 ]
do
        echo $*
        shift
done
                                                                                        
[root@qfedu.com  ~]# ./demo.sh a b c d e f g h
a b c d e f g h
b c d e f g h
c d e f g h
d e f g h
e f g h
f g h
g h
h
[root@qfedu.com  ~]# vim shift.sh             
#!/bin/bash
until [ -z "$1" ]
do
        echo "$1"
        shift
done
echo
                                                                                        
[root@qfedu.com  ~]# ./shift.sh a b c d e f g h  
a
b
c
d
e
f
g

三、Shell 编程之函数

Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可

1、定义函数

可以带function fun() 定义也可以直接fun() 定义,不带任何参数。

# 方法一
function name() {
  commands
  [return value]
}
# 方法二
name() {
  commands
  [return value]
}
  • function 是 Shell 中的关键字,专门用来定义函数;

  • name 是函数名;

  • commands 是函数要执行的代码,也就是一组语句;

  • return value 表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。

  • 由 { } 包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。

  • 函数的优势

    • 方便n次使用减少代码量使之方便整洁。
    • 当需要修改里面的重复代码时,只需要修改一次函数即可实现需求;
    • 将函数写进文件,需要时直接通过文件调用

2、调用函数

1、执行不带参数的函数

直接输入函数名即可,不需要带括号,

functionName
  • 执行函数时函数名前的关键字function和函数名后面的()均不需要带
  • 函数的定义必须要在执行的程序前定义或加载

2、执行带参数的函数

functionName arg1 arg2
  • Shell中的位置参数($1/$2.../#/?/$@)均可以做为函数的参数进行传递
  • $0比较特殊仍然是父脚本的名称
  • 此时父脚本的参数会临时被函数的参数所掩盖或隐藏
  • 函数的参数变量是在函数体内里面进行定义

调用函数示例

[root@qfedu.com  ~]# cat testfunction.sh
#!/bin/bash
# first function
function HelloWorld() {
  echo "Hello world"
}
# second function
Welcome() {
  echo "Welcome to qfedu"
}
# third function
function HelloShell {
 echo "Hello Shell"
}
# file functions
HelloWorld               # 调用函数
Welcome
HelloShell
[root@qfedu.com  ~]# bash testfunction.sh
Hello world
Welcome to qfedu
Hello Shell

从文件中调用函数示例

[root@qfedu.com  ~]# cat filefunction.sh
function Sum () {
 for((i=1;i<=100;i++))
  do
    ((sum=sum+i))
  done
  echo '{1..100} sum is :' $sum
}
[root@qfedu.com  ~]# cat filefunctionfromfile.sh
#!/bin/bash
path="/root/Test/filefunction.sh"
if [ -f ${path} ]
   then
    source $path # 加载函数
    Sum          # 调用函数
else
   echo "file not exist or error"
fi
[root@qfedu.com  ~]# bash filefunctionfromfile.sh
{1..100} sum is : 5050

函数参数传递示例

[root@qfedu.com  ~]# cat functionwithargs.sh
#!/bin/bash
function Add () {    # 定义函数
  ((sum=$1+$2))
  echo "$1 + $2 sum is" ${sum}
}
Add $1 $2           # 调用函数并传递参数

[root@qfedu.com  ~]# bash functionwithargs.sh 100 150
100 + 150 sum is 250
[root@qfedu.com  ~]# bash functionwithargs.sh 509 150
509 + 150 sum is 659

3、return 返回函数结果

注意return的数字必须是数字用引号包裹也一样

[root@qfedu.com  ~]# cat functionwithreturn.sh
#!/bin/bash
function TestReturn() {
  if [ -d $1 ];then
      return "122"
   else
      return "222"
  fi
}

TestReturn $1
result=$? # 获取函数返回值
if [ ${result} == "122" ]
 then
   echo "$1 exist ,return value is:" ${result}
 else
   echo "$1 not exist ,return value is:" ${result}
fi

[root@qfedu.com  ~]# bash functionwithreturn.sh /etc/sysconfiggg
/etc/sysconfiggg not exist ,return value is: 222
[root@qfedu.com  ~]# bash functionwithreturn.sh /etc/sysconfig
/etc/sysconfig exist ,return value is: 122

在该示例中,主要通过 $? 获取返回值,但返回值的范围只能是 0~255

4、echo 返回函数结果

[root@qfedu.com  ~]# cat functionwithecho.sh
#!/bin/bash
function TestReturn() {
  if [ -d $1 ];then
      echo "122"
   else
      echo "222"
  fi
}

result=$(TestReturn $1) # 获取函数返回值
if [ ${result} == "122" ];then
   echo "$1 exist ,return value is:" ${result}
 else
   echo "$1 not exist ,return value is:" ${result}
fi

[root@qfedu.com  ~]# bash functionwithecho.sh /etc/sysconfig
/etc/sysconfig exist ,return value is: 122
[root@qfedu.com  ~]# bash functionwithecho.sh /etc/sysconfiggg
/etc/sysconfiggg not exist ,return value is: 222

该示例中,主要使用 $() 获取返回值,在该方法中,没有范围限制,是一种比较安全的返回方式。

[root@qfedu.com  ~]# cat functionwithecho.sh
#!/bin/bash
function TestReturn() {
  if [ -d $1 ]
    then
      echo "$1 exist"
   else
      echo "$1 not exist"
  fi
}

result=$(TestReturn $1) # 获取返回值,返回的结果是字符串
if [ "${result}" == "$1 exist" ]
 then
   echo "$1 exist ,return value is:" ${result}
 else
   echo "$1 not exist ,return value is:" ${result}
fi

[root@qfedu.com  ~]# bash functionwithecho.sh /etc/sysconfiggg
/etc/sysconfiggg not exist ,return value is: /etc/sysconfiggg not exist
[root@qfedu.com  ~]# bash functionwithecho.sh /etc/sysconfig
/etc/sysconfig exist ,return value is: /etc/sysconfig exist

5、全局变量和局部变量

全局变量在shell 脚本中任何地方都能使用;局部变量在函数内部使用,声明前加一个 local 就好

[root@qfedu.com  ~]# cat test3.sh
function fun() {
    a=$[ $b + 5 ]
    c=$[ $a * 2 ]
}
a=4
b=6
fun

if [ $a -gt $b ]
then
    echo "$a is larger than $b"
else
    echo "$a is smaller than $b"
fi

function fun() {
    local  a=$[ $b + 5 ]
    c=$[ $a * 2 ]
}
a=4
b=6
fun

if [ $a -gt $b ]
then
    echo "$a is larger than $b"
else
    echo "$a is smaller than $b"
fi

[root@qfedu.com  ~]# bash test3.sh 
11 is larger than 6
4 is smaller than 6

6、递归函数纯了解

[root@qfedu.com  ~]# cat test6.sh
function factorial() {
    if [ $1 -eq 1 ]
    then
        echo 1
    else
        local temp=$[ $1 - 1 ]
        local result=$(factorial $temp)
        echo $[ $result * $1 ]
    fi
}

read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"

[root@qfedu.com  ~]# bash test6.sh
Enter value: 5
The factorial of 5 is: 120

扩展

内建命令

因为安装了shell而产生的命令