您的位置:首页 > 运维架构 > Shell

linux之基础shell脚本编程3 函数数组

2016-08-24 19:53 966 查看
本章主要写shell有关函数数组使用





七 函数

7.1 函数的介绍

函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。 

它与shell程序形式上是相似的,不同的是它不是一个单独的 进程,不能独立运行,而是shell程序的一部分。 

函数和shell程序比较相似,区别在于: Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改

7.2 函数的定义

函数由两部分组成:函数名和函数体。 
#语法一: 
function f_name {
   ...函数体... 
} 
#语法二: 
function f_name () {
   ...函数体... 
} 
#语法三: 
f_name (){
   ...函数体... 
}


7.3 函数的定义和使用:

 

可在交互式环境下定义函数 

可将函数放在脚本文件中作为它的一部分 

可放在只包含函数的单独文件中 

调用:函数只有被调用才会执行; 

调用:给定函数名,函数名出现的地方,会被自动替换为函数代码

函数的生命周期:被调用时创建,返回时终止

函数一直保留到用户从系统退出,或执行了 unset name 命令

7.4 函数有两种返回值

函数的执行结果返回值: 

  (1) 使用echo或printf命令进行输出 

  (2) 函数体中调用命令的输出结果 

函数的退出状态码: 

  (1) 默认取决于函数中执行的最后一条命令的退出状态码 

 (2) 自定义退出状态码,其格式为: 

    return  从函数中返回,用最后状态命令决定返回值 

    return 0  无错误返回。 

    return 1-255  有错误返回

7.5 在脚本中定义函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用 

调用函数仅使用其函数名即可

比如:
#!/bin/bash
hello(){
    echo "hello to linux"
}
hello  #直接调用函数


7.6 使用函数文件

可以将经常使用的函数存入函数文件,然后将函数文件载入shell。 

文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main 

一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。

若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。

7.6.1 创建函数文件
[root@centos7 test]# vim functions.main

#!/bin/bash
#functions.main
#author nineven
findit(){
    if [ $# -lt 1 ] ; then
        echo "Usage:findit file" 
        return 1
    fi
    find / -name $1 -print
}


7.6.2 载入函数文件

函数文件已创建好后,要将它载入shell 

定位函数文件并载入shell的格式: . filename 或 source   filename  

注意:此即<点> <空格> <文件名> 这里的文件名要带正确路径 

比如:上例中的函数,可使用如下命令:
[root@centos7 test]# . ./functions.main



[root@centos7 test]# source ./functions.main


7.6.3 检查载入函数

使用set命令检查函数是否已载入

set命令将在shell中显示所有的载入函数

比如:



7.6.4 执行shell函数

要执行函数,简单地键入函数名即可
[root@centos7 test]# findit te.sh
/testdir/shell/test/te.sh
/root/Desktop/tmp/testdir/shell/test/te.sh
/root/te.sh
/tmp/testdir/shell/test/te.sh
[root@centos7 test]#


7.6.5 删除shell函数

删除函数,使其对shell不可用 。使用unset命令完成此功能. 命令格式为: unset  function_name

比如: $unset findit 再键入set命令,函数将不再显示
[root@centos7 test]# findit te.sh
/testdir/shell/test/te.sh
/root/Desktop/tmp/testdir/shell/test/te.sh
/root/te.sh
/tmp/testdir/shell/test/te.sh
[root@centos7 test]# 
[root@centos7 test]# unset findit
[root@centos7 test]# findit te.sh
bash: findit: command not found...
[root@centos7 test]#


7.7 函数参数

函数可以接受参数,传递参数给函数,调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“func arg1 arg2 ...” 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

7.8 函数变量

变量作用域:

 环境变量:当前shell和子shell有效

 本地变量:只在当前shell进程有效,为执行脚本会启动,专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数

 局部变量:函数的生命周期;函数结束时变量被自动销毁 

注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量。 

在函数中定义局部变量的方法 local NAME=VALUE

7.9 函数递归:

函数递归: 

  函数直接或间接调用自身 

  注意递归层数 

递归实例: 阶乘是基斯顿·卡曼于1808年发明的运算符号,是数学术语 一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有0的阶乘为1。自然数n的阶乘写作n! ; n!=1×2×3×...×n。 

阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n ; n!=n(n-1)(n-2)...1 ; n(n-1)! = n(n-1)(n-2)!

比如:
#!/bin/bash 
fact() { 
  if [ $1 -eq 0 -o $1 -eq 1 ]; then 
    echo 1 
  else 
    echo $[$1*$(fact $[$1-1])] fi } 
fact $1


 

实战训练9

1、写一个服务脚本/root/bin/testsrv.sh,完成如下要求 

(1) 脚本可接受参数:start, stop, restart, status 

(2) 如果参数非此四者之一,提示使用格式后报错退出 

(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功” 考虑:如果事先已经启动过一次,该如何处理? 

(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成” 考虑:如果事先已然停止过了,该如何处理? 

(5) 如是restart,则先stop, 再start 考虑:如果本来没有start,如何处理? 

(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示 “SCRIPT_NAME is running...” 如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...” 其中:SCRIPT_NAME为当前脚本名 

(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理

#!/bin/bash
syon(){
    if [  -e /var/lock/subsys/$0 ];then
        echo 1
    else
        echo 0
    fi
}
starts(){
    if [ $(syon) -eq 1 ];then
        echo "服务已经启动过了"
    else
        if  touch /var/lock/subsys/$0 &> /dev/null ;then
            echo "$1启动成功"
        else
            echo "$1启动失败"
            exit 1
        fi
    fi
}
stops(){
    if [ $(syon) -eq 0 ] ;then
         echo "服务没有启动"
    else
          if  rm -f /var/lock/subsys/$0 &>/dev/null ;then
            echo "停止成功"     
          else
            echo "停止失败$1"
            exit 1
          fi
    fi
}
restarts(){
    stops ",服务无法重新启动"
    starts "服务正在重新重启中,"
}
statuss(){
    if [ $(syon) -eq 0 ] ;then
        echo "服务没有启动"
    else
        echo "服务已经启动过了"
    fi
}
case $1 in
    start)
        starts
        ;;
    stop)
        stops
        ;;
    restart)
        restarts
        ;;
    status)
        statuss
        ;;
    *)
        echo "error ,please Usage: $0 {start|stop|restart|status}"
esac




2、编写一个脚本/root/bin/copycmd.sh 

(1) 提示用户输入一个可执行命令名称; 

(2) 获取此命令所依赖到的所有库文件列表 

(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下; 

如:/bin/bash ==> /mnt/sysroot/bin/bash /usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd 

(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下: 

如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ldlinux-x86-64.so.2 

(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出

#!/bin/bash
flag=1
read -p "please input a path that you want to copy : " mubiaodir
panduanlujing(){
    path=$(dirname $mubiaodir$ki)
    if [ ! -e $path ];then
        mkdir -p $path
    fi
    cp -fa $ki $mubiaodir$ki &> /dev/null || flag=0
}
kuwenjian(){
    for ki in $(ldd $(which --skip-alias $cmdname) | sed -rn "s@.*[[:space:]]([/].*)[[:space:]].*@\1@p") $(which --skip-alias $cmdname);do
        panduanlujing
    done
}
while true ;do
read -p "please input a command name , quit is exit:" cmdname
if [ "$cmdname" == "quit" -o "$cmdname" == "exit" ];then
    break
else
    if (ldd $(which --skip-alias $cmdname)) &> /dev/null ;then
        kuwenjian
        [  $flag -eq 1 ] && echo "复制成功" || echo "权限不足,复制失败"
    else
        echo "错误的命令名称"
    fi
fi
done


3、写一个函数实现两个数字做为参数,返回最大值 
#!/bin/bash
maxnum(){
expr $1 + $2 &> /dev/null && echo $(( $1 > $2 ? $1 : $2 )) || echo "参数错误"
}
maxnum $1 $2


4、写一个函数实现数字的加减乘除运算,例如输入 1 + 2,,将得出正 确结果 
#!/bin/bash
yunsuan(){

    case $key in
        *)
            echo "$a$key$b=$[a $key b]"
            ;;
        -)
            echo "$a$key$b=$[a $key b]"
            ;;
        +)
            echo "$a$key$b=$[a $key b]"
            ;;
        /)
            echo "$a$key$b=$[a $key b]"
            ;;
    esac
}

read -p "please input agr1 [+|-|*|/] agr2 :" a key b
if [ "$key" == "*" ];then
    if expr $a \* $b &> /dev/null ;then
            yunsuan
    else
            echo "输入错误"
    fi
else

    if  expr $a $key $b  &> /dev/null ;then
        yunsuan
    else
        echo "输入错误"
    fi
fi


5、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子 繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1 、1、2、3、5、8、13、21、34、……,斐波纳契数列以如下被以递归的 方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2) 写一个函数,求n阶斐波那契数列 

#!/bin/bash
fei(){
    if [ $1 -eq 0 ] ;then
        echo 0
    elif [ $1 -eq 1 ];then
        echo 1
    else
        echo $[$(fei $[$1-1]) + $(fei $[$1-2])]
    fi
}
if [ $# -eq 0  ] ;then
    echo "请传一个个整数作为参数"
else
    fei $[$1-1]
fi

#!/bin/bash
a=0
b=1
fei(){
for((i=3;i<=$1;i++)){
    let c=a+b,a=b,b=c
    echo -n "$c "
}
}
if expr $1 + 1 &> /dev/null ;then
if [ $1 -eq 1 ];then 
    echo $a
elif [ $1 -eq 2 ];then
    echo $a $b
elif [ $1 -ge 3 ];then
    echo $a $b `fei $1`
else
    echo error
fi
else
echo error
fi




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

#!/bin/bash
move(){
    echo  "将第$1个盘子从 $2 --> $3 "
}
han(){
    if [ $1 -eq 1 ];then
        move 1 $2 $4
    else
        han $[$1-1] $2 $4 $3
        move $1 $2 $4
        han $[$1-1] $3 $2 $4
    fi
}
read -p "请输入盘数:" n
han $n a b c




八 数组

变量:存储在单个元素的内存空间

属组:存储多个元素的连续的内存空间,相当于多个变量的集合

数组名和索引

 索引:编号从0开始,属于数值索引

 注意:索引可支持使用自定义格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持

bash的属组支持稀疏格式(索引不连续)

8.1 定义数组

1,声明数组:

 declare -a ARRAY_NAME

 declare -A ARRAY_NAME:关联数组

2,数组元素的赋值:

 (1)一次只赋值一个元素

    ARRAY_NAME[INDEX]=VALUE

    week[0]="oneday"
    week[2]="twoday"


 (2)一次性赋值全部元素
    ARRAY_NAME=("one" "two" "three")


 (3)只赋值特定元素

    ARRAY_NAME=([0]="val1" [2]="val2"...)


  (4)交互式数组赋值

    read -a ARRAY

3,引用数组

引用数组元素: ${ARRAY_NAME[INDEX]}  省略[INDEX]表示引用下标为0的元素

${ARRAY_NAME[*]}  输出数组所有内容

${ARRAY_NAME[@]}  输出数组所有内容

数组的长度(数组中元素的个数)

    ${#ARRAY_NAME[*]}

    ${#ARRAY_NAME[@]}

比如:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
declare -a num
let num[0]=min=max=$RANDOM
for i in {1..9};do
    num[$i]=$RANDOM
    [ ${num[$i]} -gt $max ] && max=${num[$i]}
    [ ${num[$i]} -lt $min ] && min=${num[$i]}
done
echo ${num[*]}
echo "max=$max  min=$min"




练习: 编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和 
#!/bin/bash
declare -i lines=0
declare -a filename
filename=(/var/log/*.log)
for i in $(seq 0 $[${#filename[*]}-1]);do
    if [ $[$i%2] -eq 0 ];then
        let lines+=$(cat ${filename[$i]} | wc -l)
    fi  
done
echo "lines is $lines"


4,数组数据处理

a,引用数组中的元素

    所有元素: ${ARRAY_NAME[*]}  ${ARRAY_NAME[@]}

    数组切片: ${ARRAY_NAME[@]:offset:number}

        offset: 要跳过的元素个数

        number: 要取出的元素个数

     取出偏移量之后的所有元素: ${ARRAY_NAME[@]:offset}



b,向数组中追加元素: ARRAY_NAME[${#ARRAYNEM[*]}]



c,删除数组中的某元素:导致稀疏格式:unset ARRAY_NAME[INDEX]



d,关联数组:

declare -A ARRAY_NAME

ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)

实战训练10

1,生成10个随机数,采用冒泡算法进行升序或降序排序
#!/bin/bash
declare -i n=10
maopao(){
    for i in $(seq 1 $[$n-1]);do
    for j in $(seq 0 $[$n-$i-1]);do
        if [ ${k[$j+1]} -gt ${k[$j]} ];then
            tmp=${k[$j+1]}
            k[$j+1]=${k[$j]}
            k[$j]=$tmp
        fi
    done
    done
}
declare -a k
for i in  $(seq 0 $[$n-1]);do
    k[$i]=$RANDOM
done
echo "原来随机数:${k[*]}"
maopao
echo "排序后的数:"${k[*]}




2,手动输入数字排序
#!/bin/bash
echo  "please input  number and split with space"
read -a k
n=${#k[*]}
for((i=0;i<n;i++)){
for((j=0;j<n-i-1;j++)){
[ ${k[$j]} -gt ${k[$j+1]} ] && { tmp=${k[$j]};k[$j]=${k[$j+1]};k[$j+1]=$tmp; }
}
}
echo ${k[@]}






内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: