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


把那些在脚本中重复出现并且没有任何改变的代码,封装起来,在适当的场景中调用执行;

程序员将这种被封装起来的代码称为功能体,或者叫模块;

function —— 函数


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


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


函数是在shell程序的当前shell中运行的;

bash

  bash script_file

  function


定义函数:

  函数是由两部分组成:

    函数名称 + 函数体(能够实现独立功能的shell语句块)


  语法一:

    function func_name {

      函数体

    }


  语法二:

    func_name() {

      函数体

    }

    注意:函数名和()之间不能加空白字符;


  注意:函数可以在交互式环境下定义,也可以在脚本中定义;


函数的使用

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


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


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


使用set命令可以查看所有当前shell中生效的函数;

使用unset命令可以撤销已经定义的函数;


函数的返回值:

  两种返回值:

    函数的执行结果的返回值:

      1.在函数体中使用了echo或printf命令输出的结果;

      2.在函数体中某些命令输出的结果;

    函数的状态返回值:

      1.函数中最后一条命令的执行状态返回值;

      2.自定义退出状态码:

        return [n]

          n:0-255 (1 2 127尽可能不使用)

          0: 表示无错误返回

          1-255:有错误返回


        注意:只要函数在执行时,遇到了return命令,不管函数中的命令语句是否全部执行完成,立刻退出函数;


函数的生命周期:

  从被调用开始,到遇到return命令或全部的语句执行完成为止;


函数的实参

  在函数体中,可以使用$1,$2,..位置变量为函数提供参数;还可以使用$*或$@的方式引用所有位置参数;还可以使用$#计算为函数传递的参数个数;


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


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


变量:

  shell中的变量为弱变量

    1.无需事先声明

    2.无需指定变量类型,默认为字符型


  变量分类:

    环境变量:

      当前shell及子shell

    本地变量:

      当前shell

    局部变量:

      local VAR_NAME=VALUE

      当前函数体

    位置变量

    特殊变量


建议:手动撤销自己定义或声明的所有变量;


函数的递归调用

  简单来说,就是在函数体中调用函数自身;


  阶乘:

    N!=N*(N-1)!=N*(N-1)*(N-2)!=...=N*(N-1)*(N-2)*...*2*1

  shell代码:

#!/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



  斐波那契数列(黄金分隔数列):

      1 1 2 3 5 8 13 21 34 55 ...

    假设兔子出生一个月之后才会有繁殖能力:

      N=N-1 + N-2


    shell代码:

#!/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



汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着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


文件abc.txt的内容如下:

2,3,4,5,6

B,c,d,e,f

6,7,8,9,10

f,g,h,i,j

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


回顾:

  shell脚本编程之函数


练习:

  写一个脚本:

    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


总结:

写一个脚本的步骤:

  1.构建程序主体

    默认是顺序执行结构

    根据需求添加相应的选择结构和循环结构

  2.确定完成某功能所需的命令

  3.调试(bash -x /PATH/TO/SCRIPT_FILE)

  4.写明帮助信息