驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
shell函数中不同于现代化编程语言的一些坑
/  

shell函数中不同于现代化编程语言的一些坑

Shell 的函数就是一个大坑,坑的人不要不要的。

函数形参

不同于目前流行的Java等语法,shell 的方法形参在声明的时候不需要显示声明的,调用的时候也是直接写在方法名后面,通过空格分隔。

比如定义一个两数求和,传统的写法可能类似如下所示:

sum(a,b){
	return a+b
}

res=sum(1,2)
echo res

但是shell的写法是:

sum(){
	return $(($1+$2))
}

sum 1 2
echo "$?"

shell 的方法有如下一些奇怪的地方

  • 形参不需要显示的描述,调用的时候通过$1~$n进行获取
  • 整数的加减运算是需要通过((expr)) 包起来
  • 通过$((expr))获取到运算后的值
  • 调用的时候,只需要方法名即可,同时多个参数通过空格区分
  • $? 表示的含义是:
    • 上条指令exit的退出状态,通过 0 表示成功,1 表示失败,此处可以参考:参考
    • 上条指令的return出来的返回值。

函数返回值

shell 中函数的返回值只能是数值类型,是的,你没有看错就是数值类型。当你返回字符串的时候,会报错: return: xxx: numeric argument required

下面的测试代码,大家可以执行。

a(){
  return "ab"
}

b(){
	return "123"
}

#调用a,报错:ab: numeric argument required
a
#调用b,成功,但是无任何内容输出
b

以下为江湖传言:shell 的返回值设计的目的不同于其他语言,其表示的是函数的执行结果,0 表示成功,1 表示失败。

那么如何接受函数的返回值了,单函数的返回值是数值类型的时候,有 3 种办法:

其实也是有 3 种方法进行接收

  • 通过$(expr)进行标准输出内容的接收
a(){
  echo 1
}

b=$(a)
echo "$b"
  • 通过$?接收上一条指令的返回值
a(){
  return 1
}

a
echo "$?"
  • 通过全局变量
a(){
  b=1
}

a
echo "$b"

当返回值是字符的时候,上述的第二种写法就不可以了,需要使用第一种和第三种写法。

  • 使用全局变量来代替
a(){
  name="ab"
}

# 直接调用全局变量 $name
echo "name:$name"
  • 通过$(method)来接收函数中的输出,其中$(expr)是固定用法,表示接受表达式输出到标准输出的值。
a(){
  echo "ab"
}

# 执行该操作后的值就是 ab
re=$(a)
echo "name:$re"

函数调用

假如有一个函数是sum,有 3 种情况分别对应调用函数时的写法

  • 不需要接收返回值的时候,直接一行 sum 就行了。
  • 需要接收返回值的时候
    • 返回值是通过return返回的数值类型的时候,也是直接写,然后通过$?接收
    • 返回值是通过echo输出的时候,需要通过:$(expr)进行接收

上文已经提到过了,函数的返回值只能是数值。在接收返回值上,也同当前的通用语言不同,比如下面这个做法就无法获取到返回值。

错误写法:

sum(){
  return 5
}
b=sum
echo "b:$b" 
#输出内容是 b=sum

因为 shell 认为你是在进行赋值操作,将字符串sum赋值给 b,正确的做法是通过$?来获取返回值。

正确的写法是

sum(){
  return 5
}
sum
b=$?
echo "b:$b" 
#输出内容是 b:5

对于通过echo输出结果的方法,函数调用的时候,需要通过$((expr))

sum(){
	echo 667 #1
  return "5"
}
# 通过$(method)调用
b=$(sum)
echo "b:$b"

通过b=$(sum)获取的是函数sum的标准输出,说人话就是类似echo在控制台的输出内容。

综合应用

案例 1:

sum(){
  echo 6
  return "5"
}
a=$(sum)
echo "b:$?"
echo "a:$a"

结果就是

b:5
a:6
  • $?表示的是最近的return的值或者exit的值,很显然就是return的 5
  • $(expr)获取的是echo所以是 6

案例 2:

sum(){
  echo 6
  echo "i love wmm"
  echo "i love kotlin"
  return "5"
}
a=$(sum)
echo "$a"

#输出结果是
6
i love wmm
i love kotlin

总结

  • 函数的参数不需要显示声明,需要传递参数的时候,写在函数名的后面,多个参数值通过空格分开
  • 函数体中,如果需要使用参数,通过$0-$n来调用。
  • 函数的返回值只能是数值类型,不能是字符串类型。
  • 调用函数时,如果不需要返回值,那么只需要method [param]即可。
  • 将函数调用后赋值给a,其表示的函数的意义是函数执行过程中的标准输出,如果要获取返回值需要通过$?
  • 调用函数时,如果需要函数的标准输出内容,那么需要通过$(method)进行标准输出的接收,此时函数中的标准输出就不会打印到控制台,而是赋值到变量中了

$?表示的含义其实是上一次命令的返回值或者退出值,所以假如要获取返回值,建议在函数执行的下一行使用该变量,避免中间执行其他指令,将返回值(退出值)覆盖。

re=$(method) 该调用会将函数中本身的控制台输出吃掉,然后复制给参数re

骐骥一跃,不能十步。驽马十驾,功在不舍。