首页 > 代码库 > 循环与函数及相关例子

循环与函数及相关例子

下面写的是case分支选择结构、while循环、until循环、循环控制语句(continue、break)、函数(定义、使用、返回值、实参、递归调用)

一些基本知识点如下:

bash -n 用来判断有无语法错误

=~ 用来判断前后是否匹配

# bash -x little.sh 显示运行的具体过程及其输出结果

取得文件大小的两种方式:

# wc -c < /usr/local/test

# stat -c %s /usr/local/test


一、case分支选择结构

1、case格式: case 词 in [模式 [| 模式]...) 命令 ;;]... esac

 case 变量引用 in     //变量引用为if中的$1等

   模式1)

      分支1

      ;;

   模式2)

      分支2

      ;; 

   ……

   *)

     默认分支

     ;;

 esac

2、模式(PATTERN)可以为如下三类:

 (1)、普通文本文件

 (2)、GLOBBING风格的分隔符: *:为任意长度任意字符; ?:任意的单个字符; []:范围内的任意单个字符; [^]:范围外的任意单个字符

 (3)、|:或

  

示例1:写一个脚本:提示用户输入信息,然后判断用户输入的信息是否合法;

#!/bin/bash

#

read -p "Please make your choice[yes of no]: " CHOICE


case $CHOICE in

yes)

echo "right." 

;;

no)

echo "wrong."

;;

*)

echo "Unknown."

;;

esac

例子中的模式,如果不区分大小写可以为:yes|YES)和[Yy][Ee][Ss])


3、case的if实现方式如下:

if CONDITION1 ; then

STATEMENT

elif CONDITION2 ; then

STATEMENT

elif CONDITION3 ; then

STATEMENT

...

else

STATEMENT

fi


4、if的多分支结构和case的多分支结构的区别:

相同点:1)都是条件为真,执行条件分支语句;条件为假即不执行; 2)都可以设置默认分支语句,即所有条件都不匹配的时候,所执行的语句

不同点:1)if是根据命令执行状态返回值来判断正确与否,case是根据变量的值的取值内容是否匹配模式来判断正确与否;2)case的每个分支都必须用“;;”来结束


# touch ling 用来创建一个文件ling

示例2:管理用户账户的脚本,case实现a添加和d删除

#!/bin/bash

#

if [ $# -lt 2 ] ; then

  echo "Usage: $(basename $0) -a User1,User2,...,UserN | -d User1,User2,...,UserN."

  exit 5

fi


case $1 in

  -a)

 for I in $(echo $2 | tr ‘,‘ ‘ ‘) ; do

   if id $I &> /dev/null ; then

     echo "$I exists already."

   else

     useradd $I

     echo $I | passwd --stdin $I &> /dev/null

     echo "Create $I successfully."

   fi

 done

  ;;

  -d)

 for J in $(echo $2 | tr ‘,‘ ‘ ‘) ; do

   if id $J &> /dev/null ; then

     userdel -r $J

     echo "Delte $J finished."

   else

     echo "User $J does not exist."

   fi

 done

  ;;

  *)

 echo "Usage: $(basename $0) -a User1,User2,...,UserN | -d User1,User2,...,UserN."

 exit 6

esac


二、四种循环控制语句

1、while循环

while的格式: while 命令; do 命令; done

while CONDITION ; do

  循环体

done

进入循环条件:CONDITION一直为真

退出循环条件:CONDITION为假


2、until循环

until的格式: until 命令; do 命令; done

until CONDITION ; do

  循环体

done

进入循环条件:CONDITION一直为假

退出循环条件:CONDITION为真


while CONDITION ; do 循环体 ; done

相当于

until ! CONDITION ; do 循环体 ; done

注意:对于 while和until两个循环结构来讲,如果要实施变量增量操作,必须手动给出。


示例3:利用while和until循环结构,计算100以内所有整数的和;

#!/bin/bash

#

declare -i I=1

while [ $I -le 100 ] ; do

let SUM+=$I

let I++

done

echo $SUM


#!/bin/bash

#

declare -i I=1

until [ $I -gt 100 ] ; do

let SUM+=$I

let I++

done

echo $SUM



3、循环控制语句:continue、break

(1)、continue

  continue格式:continue [n]

  提前结束第n层的本次循环,直接进入下一轮的条件判断,若符合进入条件,否则开启下一轮循环

(2)、break

  break的格式:break [n]

  提前结束第n层循环,即以后的循环都不做,不再继续后续循环


无限循环用法:

while true ; do 

  循环体

done


until false ; do

  循环体

done

在此类循环语句中,必须适当的使用continue和break,以保证循环不会一直持续下去;



示例4:

#!/bin/bash

#判断是否存在

if [ ! -e $1 ] ; then

  echo "$1 not exists."

  exit 4

fi 

#判断是否为文本文件

if [ ! -f $1 ] ; then

  echo "Please supply a text file for checking syntax."

  exit 5

fi

#判断有无语法错误

until false ; do 

  vim + $1

  if bash -n $1 ; then

    break

  else

    continue

  fi

done



4、遍历

能够实现遍历功能的while循环如下:

while read LINES ; do 

  循环体

do < /PATH/FROM/SOMEFILE


until ! read LINES ; do 

  循环体

do < /PATH/FROM/SOMEFILE



5、select循环

select循环主要是用来【创建菜单式列表】,供用户进行选择;其中列表按照数字顺序进行排列,我们只要选择数字即可;内容在错误输出2上输出。一般来讲,select与case一起使用。同时,select是一个无限循环,所以必须使用break命令用于退出该循环,或者可以使用exit

select格式: select NAME [in 词语 ... ;] do 命令; done

select NAME [in LIST] ; do

  命令

done


示例5:

#!/bin/bash

select I in meat soap rice ; do

  case $1 in 

    meat)

      echo "I like it"

      break

      ;;

    soap)

      echo "I don`t like it."

      continue

      ;;

    *)

      exit

      ;;

  esac

done


示例6:编写一个shell脚本将/usr/local/test命令下大于100k的文件转移到/tmp目录下。

#!/bin/bash

#

FPATH="/usr/local/test"

DEST=/tmp

for i in $FPATH/* ; do

if [ -f $i ] ; then

    if [ $(wc -c < $i) -gt 102400 ] ; then

      ls -l $i

    fi

  fi

done

#!/bin/bash

#

FPATH="/usr/local/test"

destination=/test

for I in $FPATH/* ; do

   if [ -f $I ] ; then

     if [ $(wc -c < $FPATH/$I) -gt 102400 ] ; then

       mv $FPATH/$I $destination

       ls -l $I

     fi

   fi

done



示例7:

写一个具有systemV风格的脚本:给脚本传递一些参数,如start、stop、restart、status。

myservice.sh

#!/bin/bash

#

lockfile="/var/lock/subsys/$(basename $0)"

case $1 in

start)

if [ -f $lockfile ] ; then

echo "服务已经启动...."

else

touch $lockfile

echo "服务正在启动...."

fi

;;

stop)

if [ -f $lockfile ] ; then

rm -f $lockfile

echo "服务已经停止...."

else

echo "服务尚未启动..."

fi

;;

restart)

if [ -f $lockfile ] ; then

rm -f $lockfile

echo "服务已经停止...."

else

echo "服务尚未启动..."

fi

if [ -f $lockfile ] ; then

echo "服务已经启动...."

else

touch $lockfile

echo "服务正在启动...."

fi

;;

status)

if [ -f $lockfile ] ; then

echo "服务已经启动...."

else

echo "服务已经停止...."

fi

;;

*)

echo "Usage: $(basename $0) start|stop|restart|status"

exit 5

;;

esac



示例8:创建用户,局部变量

#!/bin/bash

#

adduser() {

  for I in {1..5} ; do 

if id $1$I &> /dev/null ; then

 echo "user $1$I exists."

else 

 useradd $1$I &> /dev/null

 echo $1$I | passwd --stdin &> /dev/null

 echo "create user $1$I finished."

fi

  done

}

adduser $1


示例9:调用函数

#!/bin/bash

#

NAME=ling

func() {

  local NAME=little

  echo "func_NAME: $NAME"

}

func

echo "script_NAME: $NAME"



三、函数

1、把那些在脚本中重复出现并且没有任何改变的代码,封装起来,在适当的场景中调用执行。程序员将这种封装起来的代码称为功能体,或者叫模板。这种功能体即function,后来称之为函数。

在shell脚本编程中,函数是由若干条shell命令组成的语句块,通常用于代码重用和模块化封装。

函数里面的内容和shell的程序,其形式上是保持一致的;不同之处,就是shell代码可以直接被执行,而函数中的内容不能独立执行,只能在被调用的时候才执行。

函数是在当前shell程序中运行的,如下:

bash

  bash script_file

  function


2、定义函数

函数是由两部分组成的:即函数名称+函数体

函数体,即能够实现独立功能的shell语句块

语法一:

function func_name{

函数体

}

语法二:

func_name() { 

函数体;

}

注意:函数名和()之间不能加空白字符;函数可以在交互式环境下定义,也可以在脚本(非交互式环境)中定义。


3、函数使用

函数在定义的时候,其函数体中包含的所有命令都不会被执行;只有函数被调用的时候,才会执行其中的命令语句。

调用方式:通过直接给出函数名称的方式调用。

有很多的函数是存放于专门用于保存函数的文件中;如果想要调用这样的文件中保存的函数,使用source命令加载文件,然后再以直接给出函数名称的方式调用函数。

使用set命令可以查看所有当前shell中生效的函数;使用unset命令可以撤销已经定义的函数。


4、函数的返回值:函数也有两种返回值,具体如下

 函数执行结果的返回值:(1)、在函数体中使用了echo或printf命令输出的结果;(2)、在函数体中某些命令输出的结果。

 函数的状态返回值:(1)、函数最后一条命令的执行状态返回值;(2)、自定义退出状态码,例如 return [n] ;其中,n为0-255,但1、2、127这三个状态码尽可能不用。其中,0表示无错误输出;1-255表示有错误输出。

 注意:主要函数在执行时,遇到return命令,不管函数中的命令执行语句是否全部执行完成,都立即退出函数。


 函数的生命周期:从被调用开始,到遇到return命令或全部的语句执行完成为止。


 5、函数的实参:在函数体中,可以是使用$1,$2,$3……等位置变量为函数提供参数,还可以使用$*或$@的方式引用所有位置参数;还可以使用$#计算为函数传递参数的个数。例如:func_name($1) {}

 在调用函数的时候,直接在函数名称后面以空白字符分隔多个参数即可,比如:func_name arg1,arg2……

 传递给参数的位置参数,是调用函数的时候,“函数名称后面”的以空白字符分隔的字符串序列,跟脚本的位置参数不是一回事。


 shell中的变量为弱变量,即无需事先声明;也无需指定变量类型,默认为字符型 。

 变量分类:环境变量、本地变量、局部变量、位置变量、特殊变量。

 环境变量的生命周期为单钱shell及子shell。本地变量的生命周期为当前shell。局部变量的生命周期为当前函数体,local VAR_NAME=VALUE,局部变量local单独申请内存空间,处于外部。建议手动撤销自己定义或声明的所有变量。


6、函数递归调用:简单来说,函数的递归调用就是在函数体中调用函数自身。

示例10:阶乘:可以递归实现阶乘 N!=N*(N-1)!=……=N*(N-1)*……*2*1

#!/bin/bash

# Author: Tianyu.Zhao

#

fact(){

  if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then

    echo 1

  else

    echo "$[$1*$(fact $[$1-1])]"

  fi

}


echo -n "$1!="

fact $1

 


示例11:斐波那契数列(黄金分割数列、兔子):1 1 2 3 5 8 13 21 34 55 …… N=(N-1)+(N-2)

 echo "$[$(fbna $[$1-1])+$()]"

#!/bin/bash

# Author: Tianyu.Zhao

#

fabonacci(){

  if [ $1 -eq 1 ] ; then

    echo 1

  elif [ $1 -eq 2 ] ; then

    echo 1 

  else

    echo $[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])] 

  fi

}

#列出所有的斐波那契数列的项

for I in `seq 0 $1` ; do

  fabonacci $I

done


示例12:汉诺塔问题。汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。利用函数,实现N片盘的汉诺塔的移动步骤:

#!/bin/bash

# Author: Tianyu.Zhao

#

step=0

move(){

let step++

echo "$step:  move disk $1 $2 -----> $3"

}

hanoi(){

if [ $1 -eq 1 ];then

move  $1 $2 $4

else

hanoi "$[$1-1]" $2 $4 $3

move $1 $2 $4

hanoi "$[$1-1]" $3 $2 $4

fi

}


hanoi $1 A B C


示例13:写一个脚本,利用任意循环结构,输出每一行的第二个和第四个字符(以逗号分隔)

文件ling.txt内容如下:

1,2,3,4,5

q,w,e,r,t

A,S,D,F,G

!,@,#,$,%

#!/bin/bash

#

char() {

while read lines ; do 

  echo -n $lines | cut -d, -f2,4

done < ling.txt

}


示例14:写一个脚本实现下列功能

(1)、允许用户通过命令行传递参数,实现用户账户的管理

(2)、如果给出-a|--add选项,就创建该选项后面的用户账户

(3)、如果给出-d|--del选项,就删除该选项后面的用户账户 

(4)、如果用户给出-v|--verbose选项,就显示创建或删除信息;

(5)、.如果用户给出-h|--help选项,就显示帮助信息,并且以0作为退出状态码退出脚本的运行;

(6)、如果用户给出的其他选项,显示帮助信息,并以5作为退出状态码鬼畜脚本的运行;

#!/bin/bash

#Author: Link

#Description: administrate users

#5: no enough args

#6: error args

#

DEBUG=0

ADDUSER=0

DEUSER=0


usage(){

echo "Usage: $(basename $0) -a|--add user1,user2,... | -d|--del user1,user2,... | [-v|-verbose] | [-h|--help]"

echo

  echo "Options: "

  echo -e "  -a, --add\vCreate user from list."

  echo -e "  -d, --del\vDelete user from list."

  echo -e "  -v, --verbose\vDisplay infomation for your operating."

  echo -e "  -h, --help\vDisplay this menu."

}


createuser() {

ADDUSER_LIST=$(echo $1 | tr ‘,‘ ‘ ‘)

for I in $ADDUSER_LIST ; do

if id $I &> /dev/null ; then

[ $DEBUG -eq 1 ] && echo "$I exists."

else

useradd $I &> /dev/null

echo $I | passwd --stdin $I &> /dev/null

[ $DEBUG -eq 1 ] && echo "Create $I successfully."

fi

done

}


deleteuser() {

DELUSER_LIST=$(echo $1 | tr ‘,‘ ‘ ‘)

for J in $DELUSER_LIST ; do

if id $J &> /dev/null ; then

userdel -r $J &> /dev/null

[ $DEBUG -eq 1 ] && echo "Delete $J finished."

else

[ $DEBUG -eq 1 ] && echo "$I not exists."

fi

done

}


if [ $# -le 0 ] ; then

usage

exit 5

fi


while [ $# -ne 0 ] ; do

case $1 in

-h|--help)

usage

exit

;;

-v|--verbose)

DEBUG=1

shift

;;

-a|--add)

ADDUSER=1

ALIST=$2

shift 2

;;

-d|--del)

DELUSER=1

DLIST=$2

shift 2

;;

*)

usage

exit 6

;;

esac

done


if [ $ADDUSER -eq 1 ] ; then

createuser $ALIST

fi


if [ $DELUSER -eq 1 ] ; then

deleteuser $DLIST

fi


循环与函数及相关例子