shell函数的定义
function 函数名 { 命令序列 }
函数名() { 命令序列 }
函数的返回值
return表示退出函数并返回一个退出值,脚本中可以用$?变量显示该值
使用原则
1.函数一退出就取返回值,英文$?变量只会返回执行的最后一条指令的退出状态码
2.退出状态码必须是0-255,超出将会除以256取余
如何获取函数返回值(面试题)
方法1.函数中return返回值,函数外执行函数,再 $? 获取函数执行结果。
值必须0-255,超过取余
方法2.函数中直接 echo 输出 ,函数外可以直接函数名调用输出结果。也可以调用函数并赋值给变量,再显示变量值(更好)。
#!/bin/bash function test01 { read -p "请输入一个整数:" num return $[num * 2] } test01 #调用函数,根据返回值显示 echo $?
#!/bin/bash function test02 { read -p "请输入一个整数:" num echo $[num * 2] } #直接输入函数名调用输出结果 test02 #或者赋值给变量再显示变量 result=$(test02) # $(test02) `test02` 获取命令结果 echo $result
函数传参
位置变量 $1 $2 $3......
#!/bin/bash sum1() { sum=$[$1 + $2] #接收传参,根据传参的位置参数1 与位置参数2 相加 echo $sum } read -p "输入第一个传入参数:" first #通过键盘读入参数赋值给变量 read -p "输入第二个传入参数:" second sum1 $first $second #在调用函数时再使用变量作为传参
位置变量搭配预定义变量$#(传参个数) $@(所有参数) $0(函数本身)
··········注意传参的位置参数!
#!/bin/bash #定义函数 sum2() { #函数体内部的$1 $2 代表的是调用函数时,函数后面跟的位置参数 sum=$[$1 - $2] #$1=200 $2=100 echo $sum echo "在函数体内部的\$#代表调用函数时,函数后面跟的参数个数,当前函数后面有$#个参数" echo "在函数体内部的\$@代表调用函数时,函数后面跟的所有参数,当前函数后面的参数有:$@" echo "在函数体内部,\$0代表$0" #需要输出$0的文本,所以加上反斜杠 \ 避免$0被解释 } echo "在函数体外时,\$#代表脚本后面跟的参数个数,当前脚本后面有$#个参数" echo "在函数体外时,\$@代表脚本后面跟的所有参数,当前脚本后面参数有:$@" echo "在函数体外,\$0代表$0" #调用函数 #函数体外的$1 $2 代表的是执行脚本时,脚本后面跟的位置参数 sum2 $2 $1 #$2=200 $1=100
[xue@xue ~]$ sh 2.sh 100 200 300 400 500 在函数体外时,$#代表脚本后面跟的参数个数,当前脚本后面有5个参数 在函数体外时,$@代表脚本后面跟的所有参数,当前脚本后面参数有:100 200 300 400 500 在函数体外,$0代表2.sh 100 在函数体内部的$#代表调用函数时,函数后面跟的参数个数,当前函数后面有2个参数 在函数体内部的$@代表调用函数时,函数后面跟的所有参数,当前函数后面的参数有:200 100 在函数体内部,$0代表2.sh
全局变量/局部变量
函数默认只能在脚本内的shell环境有效(使用source执行脚本会影响系统当前的shell环境)
脚本变量默认全局有效(即函数体内外都有效)
#!/bin/bash myfun() { echo $name #空 由于未定义name name=lisi echo $name #lisi } #####main###### myfun echo $name #lisi 由于变量默认是全局变量
执行local,可将变量限定在函数体内部局部变量
执行export,将变量变为全局变量(面试会问)
#!/bin/bash myfun() { echo $name #空 由于未定义name local name=lisi echo $name #lisi } #####main###### myfun echo $name #空 由于name设定为local局部变量
递归
可以使用for循环实现相同效果,但是递归的执行效率更高
递归求阶乘
#!/bin/bash #使用函数的递归实现阶乘 jiecheng () { if [ $1 -eq 1 ];then #当递归传参=1时不再递归,返回1作为result,与传参$1相乘输出结果 echo 1 elif [ $1 -gt 1 ];then local tmp=$[$1 - 1] #tmp作为局部变量用于技术,每次递归-1,=1停止递归 local result=$(jiecheng $tmp) #递归调用 echo $[$1 * result] else echo "输入的值无效。请输入大于等于1的整数!" fi } #####main##### read -p "请输入一个大于等于1的整数:" num result=$(jiecheng $num) echo "$num的阶乘值为$result"
递归输出目录与文件
#!/bin/bash #使用函数递归目录/var/log,如果是文件直接输出文件名 #如果是目录则输出目录名且输出此目录下的所有目录和文件名 listfolder () { for folder in $(ls $1) #$(ls $1) ls用于获取文件遍历结果 $1位置变量获取需要遍历的路径 do if [ -d "$1/$folder" ];then #$1/$folder拼接完整路径,若是文件夹,$2增加空格,递归调用 echo "$2目录$folder" #显示子文件夹 #此处$2用于在子文件夹前增加空格显示层次。 listfolder "$1/$folder" " $2" #每迭代一次传参时都会增加两个两个空格。遍历子文件夹 else echo "$2文件$folder" #文件加上 $2传来的空格,显示文件名 fi done } listfolder "/var/log" "" #调用函数,第一个参数为文件夹路径,第二个参数为空,表示层次,若文件夹迭代则会增加空格显示
递归输出环境变量中目录和不可执行文件
#!/bin/bash #通过脚本输出环境变量PATH所包含的所有目录以及其中的子目录和所有不可执行文件 #定义一个遍历PATH环境变量的函数 list_path() { IFSB=$IFS IFS=$IFS':' #由于$PATH获取的环境变量用 : 分隔,修改分隔符变量IFS 使:也识别为分隔符 for F in $PATH do echo $F #echo输出$PATH环境变量中的所有文件夹 done IFS=$IFSB } #定义一个递归函数 listf() { for F in $(ls $1) do if [ -d "$1/$F" ];then echo "$2$F" listf "$1/$F" " $2" else if [ ! -x "$1/$F" ];then echo "$2$F" fi fi done } ######main###### folder=$(list_path) #调用list_path获取所有环境变量路径 for path in $folder do #对每一条路径显示 echo $path #并且调用listf递归现实子文件夹和文件 listf "$path" " " done
函数调用示例
多级调用函数将IP地址转为二进制
#!/bin/bash #将一个点分十进制格式的IP地址转换成点分二进制格式 #比如 255.255.255.255 --> 11111111.11111111.11111111.11111111 #定义一个用于十进制转换为二进制的函数 switch_bin() { NUM=$1 for i in {1..8} do SUM=$[NUM % 2]$SUM let NUM/=2 done echo $SUM } #定义一个用于分割IP的函数 SPT() { IP=$1 #获取传参IP for i in {1..4} #IP四段循环四次 do num=${IP%%.*} #num从后向前删到最后一个点,每次循环只取第一段。 IP=${IP#*.} #IP 从前向后删到第一个点, 每次循环减去头部一段 BIN=$(switch_bin num)#num为第一段取值,调用函数转换二进制 echo -n $BIN. #不换行输出转换结果( xxxxxxxx. )循环四次拼接四段 done } #####main##### read -p "请输入一个合法IP:" IP result=$(SPT $IP) echo ${result%.*} #去除最后一段的点( xxxxxxxx. → xxxxxxxx )
函数库
可以实现写好函数库,需要时直接调用,无需每次都写一遍函数
编写函数库
vim /opt/function
jiafa() { echo $[$1 + $2] } jianfa() { echo $[$1 -$2] } chengfa() { echo $[$1 * $2] } chufa() { if [ $2 -eq 0 ];then echo "除数不可为0" else echo $[$1 / $2] fi } jiecheng() { if [ $1 -eq 1 ];then echo 1 elif [ $1 -gt 1 ];then local tmp=$[$1 - 1] local result=$(jiecheng $tmp) echo $[$1 * result] else echo "输入的值无效。请输入大于等于1的整数!" fi }
调用函数库
#!/bin/bash #加载函数库文件到当前脚本的shell . /opt/function #source /opt/function = . /opt/function value1=10 value2=5 result1=$(jiafa $value1 $value2) result2=$(jianfa $value1 $value2) result3=$(chengfa $value1 $value2) result4=$(chufa $value1 $value2) result5=$(jiecheng $value1 $value2) # $(jiecheng $value1 $value2) =`jiecheng $value1 $value2` echo "加法的结果为$result1" echo "减法的结果为$result2" echo "乘法的结果为$result3" echo "除法的结果为$result4" echo "阶乘的结果为$result5"
[root@xue opt]# sh 10.sh 加法的结果为15 减法的结果为5 乘法的结果为50 除法的结果为2 阶乘的结果为3628800