linux-function

版权声明:来一来,看一看,有钱的捧个人场,没钱的你不得捧个人场 https://blog.csdn.net/wait_for_eva/article/details/85102454

结构

function func_name(){
    statement
    ...
    return code
}

架子和js的看起来一样,不过深究一下,差异还是特别的多。

position description
function 函数声明,可省略
func_name 函数名称,不可省略
() 函数标记,不可带参
statement shell语法
return 返回状态
param 参数和shell一样,使用$n进行提取

使用$?提取return的值

状态码和返回值

状态码的探究

  • 斐波那契引发的思考

写个斐波那契感受一下

#!/bin/bash
function fb(){
    if [ $1 -eq 1] || [ $1 -eq 2 ];then
        return 1
    else
        fb $(($1 - 1))
        last1=$?
        fb $(($1 - 2))
        last2=$?
        return $(($last1 + $last2))
    fi
}
fb $1
echo $?

你会发现,5以上的结果都错了。换个方式

#!/bin/bash
function fb(){
    if [ $1 -eq 2 ] || [ $1 -eq 1 ];then
        echo 1
    else
        last1=`fb $(($1 - 1))`
        last2=`fb $(($1 - 2))`
        echo $((last1 + last2))
    fi
}
fb $1

这下结果正确了,可以说,我们的逻辑没有问题,问题出在哪呢?

  • 累加和的矫正
#!/bin/bash
function sum(){
    if [ $1 -eq 1 ];then
        return 1
    else
        sum $(($1 - 1))
        last_sum=$?
        return $(($1 + $last_sum))
    fi
}
sum $1
echo $?

可以看到,在一定范围内,我们的计算结果是正确的。

查阅一下,思考一下。

$?获取的是最后一条命令的返回状态。

状态码范围是[0,255],由此,我们的sum计算结果超出255就注定了溢出被截断。

fb中,递归嵌套太多了,其中的堆栈逻辑我也不好确认。

打印计算步骤之后,发现最后一步总是+1。

也就是说,$?所谓的最后一步,并不是我们感官那样的最后一步。

不停的堆栈嵌套,加上fb中的重复计算,我们最终是丢失了我们的逻辑。

不过sum证明了,在没有重复堆栈的过程下,即使是大量的堆栈,还是会如我们所想。

  • returnexit的统一

函数和shell有太多的相似,让我不得不去分解一下这两者的区别。

毕竟参数,语法都是一致的,那么两种返回有什么不同呢。

#!/bin/bash
case $1 in 
return)
    return 1
    ;;
exit)
    exit 2
    ;;
*)
    ;;
esac

首先,有额外的收获。

case中的value字段,有天生的屏蔽作用。

除了存在通配符的语法之外,所谓关键字都只是字符而已。

虽说如此,不过尽量避免吧,误导可不好。

运行脚本,并$?查看状态,你会发现,returnexit的值并没有什么区别。

回顾起一句话:函数没有返回值就会把最后一条指令执行结果进行返回

#!/bin/bash
function show(){
    ls godme.sh
}
show

godme.sh这个文件是不存在的。

你会发现,$?是127,这个又是从哪里来的。

状态码的层级

自不量力,强行理解一波

  • 命令

命令就是可执行的东东,大致场景就在于编写和调用两个地方。

如此划分,ls之类的算作命令,函数也是命令,脚本也是一种命令。

可执行的都算作是命令,可调用,也可以自己编写。

  • 返回

每个命令呢,都有返回值,但是仅仅是自身的返回值。

其他被调用的命令的返回值,不能够决定当前命令的返回值。

不过缺省默认最后一条被调用命令返回值作为此命令的返回值。

  • 流程
Created with Raphaël 2.2.0 执行 脚本 存在exit? 返回exit状态 返回 最后一条是函数? 存在return? 返回return状态 返回之后一条命令返回值 yes no yes no yes no

说实话,就是一个中断返回的优先级问题。

类型 作用域 效果
exit shell 中断脚本并返回
return function 中断函数并返回
other shell/function 中断自身并返回
#!/bin/bash
function test(){
    exit 4
}
test
exit 3

这个脚本执行之后,返回的必定是4,因为脚本已经中断并返回了。

#!/bin/bash
function test(){
    return 4
}
test
exit 3

这个呢,中断返回的是3,脚本的事情,和函数的return有什么关系呢。

#!/bin/bash
function test(){
    return 4
}
test

恩,缺省默认,返回值当然是4啦。

简单说来,进行编程的时候,我们只涉及了逻辑的中断返回,并不管理程序的生命周期。

但是在shell中,shell作为可直接调用的程序,本身就可以中断。

function做为内部的组织结构,本身就是一段程序,本身就具有中断返回的功能。

加上shellfunction的层级关系,导致了exit可以在function中使用,才有这么奇妙而畸形的组合。

更加说明了代码块这个概念,避免重复编写,而直接引用的好处。

不过,外部逻辑影响了内部逻辑,的确是个大问题,如果是非当前脚本的函数中的exit导致脚本中断。

这问题可不小。

不过目前学的浅,不知道脚本能否调用外部函数。

但是在编写函数的时候,还是要注意别使用exit,有需求的话请再三确认。

返回值的概念

前面虽然一直说是返回值,只是为了稳住return的概念,实际上应该叫做状态码

那么,真正的返回值到底是什么呢。

说实话,我目前也不太清楚,但是$?的确是太过于繁琐了。

我个人更倾向于使用****当做返回值,也就是直接用echo``进行输出计算。

#!/bin/bash
function sum(){
    if [ $1 -eq 1 ];then
        echo 1
    else
        echo $(($1 + `sum $(($1 - 1))`))
    fi
}
sum $1

两者的统一

高级语言中,我们经常会遇见一个问题:如果是XX结果,我们需要NN。

不过尴尬的是,我们的返回值只能有一个。

当然java当中可以用数组,python中用元组。

最方便的就是用c++了,一个指针引用,内部修改就能够传达到外部。

这听起来像是if...else,不过,贴切点来说应该是try...catch

针对不同的执行结果,我们采取不同的处理办法。

if...else针对的是逻辑结构,更宽泛一些。

try...catch针对的是执行结果,更有针对性一些。

类型 说明
返回码 具体场景
返回值 具体结果

如果一开始就拿到了exception,不就是if...else了么。

  • 遍历字符串

为了演示一下功用,必须写一个脚本试试,首先先介绍一个字符串遍历的办法。

#!/bin/bash
str=$1
for index in `seq ${#str}`;do
    echo ${str:$index-1:1}
done
  • seq:这个没忘记吧,可以回顾一下
  • #:东西除了可以是注释,语法当中它可以用来计算长度
  • ${}:包括一个表达式而已
  • ${str:start:length}:在字符串strstart开始截取length个字符
  • 脚本演示

得到一个字符串,计算其中数值的和,拼接其他非数值字符串,并区分大小写和特殊字符

#!/bin/bash

function getChar(){
echo $1
case $1 in
[0-9])
    return 1
    ;;
[a-z])
    return 2
    ;;
[A-Z])
    return 3
    ;;
*)
    return 4
    ;;
esac
}
str=$1
sum=0
lowercase=""
uppercase=""
others=""
for index in `seq ${#str}`;do
    value=`getChar ${str:$index-1:1}`
    status=$?
    case $status in
    1)
        ((sum+=value))
        ;;
    2)
        lowercase=$lowercase$value
        ;;
    3)
        uppercase=$uppercase$value
        ;;
    4)
        others=$others$value
        ;;
    esac
done
echo -e "sum\t\t: $sum"
echo -e "lowercase\t: $lowercase"
echo -e "uppercase\t: $uppercase"
echo -e "others\t\t: $others"

通过代码来看,总是感觉不够明晰。

但是,退出状态这一概念,的确是明白的显露出来的。

至于通过状态选择分支,和通过条件选择分支,真就是一个模子。

我也无法说的更清楚一些。

脚本

  • 连加

补上之前欠下的,多参数连加

#!/bin/bash
function sum(){
    if [ $# -le 0 ];then
        echo "need param"
    fi
    result=0
    while [ $# -gt 0 ];do
        ((result+=$1))
        shift
    done
    echo $result
}
sum $*

递归的也可以

#!/bin/bash
function sum(){
    if [ $# -eq 2 ];then
        echo $(($2+$1))
    else
        head=$1
        shift
        echo $((head+`sum $*`))
    fi
}
sum $*

数组遍历的话,后面学到再说,要不知识体系有点混乱了,东一枪西一枪的。

小结

  • 递归

命令都可以递归使用,之前没有函数的时候,我们递归的是脚本自身

现在又function了,就别继续骚操作了。

  • 状态码

区分好exitreturn的使用范围,别不小心把脚本搞挂了。

  • 返回值

返回的话推荐echo进行值显示,有好办法的请告诉我,目前认识不多,请见谅。

return的话,尽快纠正,他是状态码返回,只能是[0,255],范围有限,容易出错。

  • case

case中的value自带转义,仅限于关键字,不过慎用。

case $param in,头上选择是变量而不是变量名,别不小心弄错了,还找不到错误。

(嗯,没错,就是我,浪费了好多时间)

  • 字符遍历

${str:index:length},截取字符串的办法,后续估计统一学习,现在先积累一些。

str变量名,index数值,length数值,别记错了。

${$str}取字符串长度。

  • 返回

虽然有状态码和返回值,概念不清晰的建议双管齐下。

先通过状态码判断场景,然后通过返回值进行操作。

要不错误状态下的echo提示语句参与了计算出错了怎么办。

可能看起来比较繁琐,但的确是个好办法,更是一个好习惯。

猜你喜欢

转载自blog.csdn.net/wait_for_eva/article/details/85102454