Shell编程之函数

本文主要对Shell中的函数进行简单总结,另外本文所使用的Linux环境为CentOS Linux release 8.2.2004,所使用的Shell为bash 5.1.0(1)-release

一、函数定义

Shell函数是一种对命令进行分组,把一组命令与单个名称相关联,以便以后使用该名称来执行的方式。它们就像常规命令一样执行。当使用Shell函数名作为简单命令名时,将执行与该函数名相关联的命令列表。Shell函数在当前Shell环境中执行,没有创建新的进程来执行。

函数定义的语法如下:

# 写法一
# function为Shell保留字,是可选的,如果提供了function保留字,()是可选的,可以省略
# fname为函数名称,在默认模式下,函数名可以是任何不被引用(unquoted)且不包含$的shell单词
# 当Shell处于POSIX模式下,函数名必须是有效的Shell名称,并且不能与特殊内置命令(special builtins)同名
# compound-command是函数体,通常是包含在{和}之间的命令列表,但也可以是复合命令
# 如果使用了function保留字,但没有提供(),则需要使用{}
# 每当函数名被指定为命令名时函数体就会被执行(函数调用)
# redirections为重定向,与Shell函数相关联的任何重定向都是在函数执行时执行的
function fname [()] compound-command [ redirections ]
# 写法二
# 没有function保留字,不能省略()
fname () compound-command [ redirections ]

几个概念解释:

  • Bash的POSIX模式(Bash POSIX Mode):在启动Bash时指定--posix选项或在Bash运行时执行set -o posix,都会使它改变那些与POSIX标准不一致的行为,从而更使Bash更符合POSIX标准。
  • 特殊内置命令:由于历史原因,POSIX标准将一些内置命令归入到特殊类别。当Bash不是在POSIX模式下执行时,这些内置命令的行为与其他Bash内置命令没有区别。
  • POSIX的特殊内置命令如下:break:.continueevalexecexitexportreadonlyreturnsetshifttrapunset
  • 重定向:在执行命令之前,它的输入和输出可以使用Shell解释的特殊符号来重定向。

注意:由于历史原因,在最常见的用法中,函数体周围的大括号{}与函数体之间必须用空白符或换行符分开。这是因为大括号是保留字并且只有当它们与命令列表用空格或其他shell元字符将隔开时才会被识别为保留字。此外,当使用大括号时,列表必须以分号、&或换行符结束。

除非出现语法错误或已经存在同名的只读函数,否则函数定义的退出状态为0。

标准写法(推荐)的函数定义示例如下:

#!/bin/bash

# 标准写法的函数定义
function func() {
    echo "这是一个函数"
}

提供了function保留字,可以省略()。示例如下:

#!/bin/bash

# 函数定义,提供function保留字,可以省略()
function func {
    echo "这是一个函数"
}

没有function保留字,不能省略()。示例如下:

#!/bin/bash

# 函数定义,没有function保留字,不能省略()
func() {
   echo "这是一个函数"
}

函数定义的时候可以嵌套。示例如下:

#!/bin/bash

function func1() {
    echo "执行函数func1"
    function func2() {
        echo "执行函数func2"
        function func3() {
            echo "执行函数func3"
        }
    }
}

二、函数调用

在函数定义之后,可以调用函数,调用函数时可以给它传递参数,也可以不传递,如果不传递参数,直接给出函数名即可:

# 函数调用,不传递参数
fname

除了DEBUGRETURN陷阱(trap)不会被继承外,shell执行环境的所有其他方面在函数和其调用者之间都是相同的,除非函数已经使用内置命令declare赋予了它trace属性,或者通过内置命令set启用了-o functrace选项(在这种情况下,所有函数都会继承DEBUGRETURN陷阱),ERR陷阱不会被继承,除非启用了shell的-o errtrace选项。

不传递参数的函数调用的示例如下:

#!/bin/bash

# 函数定义
function func() {
    echo "这是一个函数"
}
# 函数调用
func

执行结果:
1
不能把函数调用放到函数调用之前。示例如下:

#!/bin/bash

# 函数调用
func
# 函数定义
function func() {
    echo "这是一个函数"
}

执行结果:
2
由于Shell脚本是按顺序从上往下执行,函数调用在函数定义之前会先执行函数调用,由于执行func时函数还未定义,Shell会将func当成一个命令,由于在$PATH路径下找不到该命令,所以报func命令找不到的错误。所以函数调用需放在函数调用之后。

函数定义的时候可以嵌套,调用时应按执行顺序,先调用外层的函数,再依次调用内层函数。示例如下:

#!/bin/bash

function func1() {
    echo "执行函数func1"
    function func2() {
        echo "执行函数func2"
        function func3() {
            echo "执行函数func3"
        }
    }
}
func3
func2
func1
func3
func2
func3

执行结果:
3

三、函数传参

调用函数时给它传递参数,多个参数之间以空格分隔:

# 函数调用,并向它传递参数
fname param1 param2 param3 ...

当函数被执行时,函数的参数在其执行过程中成为位置参数。扩展为位置参数数量的特殊参数#会被更新以反映变化。特殊参数0不变。在函数执行时,变量FUNCNAME的第一个元素被设置为函数的名称。

位置参数和特殊参数见下表:

位置参数说明
n(n>=1)用除了单个0以外一个或多个数字表示的参数。位置参数是在shell启动时由其参数赋值的,也可以使用内置命令set来重新赋值,位置参数不能用赋值语句进行赋值,可以用内置命令setshift来设置和取消它们。当执行shell函数时,位置参数会被暂时替换为传递给函数的参数。第n个位置参数可以表示为${n},当n只由一个数字组成时,可以表示为$n,当n不只由1个数字组成时,必须表示为${n}
特殊参数说明
0($0)shell或shell脚本的名称,通常为shell脚本文件名。
#($#)位置参数的个数,用十进制表示。
*($*)从1开始的所有位置参数。当它没出现在双引号内时,每个位置参数都会扩展为一个单独的单词,在执行该操作的上下文环境中,这些单词会进一步进行单词拆分(word splitting)和文件名扩展。当它出现在双引号内时,它会扩展为一个包含每个参数的单词,每个参数的值由特殊变量IFS的第一个字符分隔。如果IFS未设置,则参数之间用空格分隔。如果IFS为空,则将参数连接起来,参数中间不使用分隔符。
@($@)从1开始的所有位置参数。在执行单词拆分的上下文环境中,将每个位置参数扩展为一个单独的单词;如果不在双引号内,这些单词将会进行单词拆分。在不执行单词拆分的上下文环境中,将每个位置参数扩展为一个单独的单词,每个位置参数之间用空格分隔。当它出现在双引号内,并且进行了单词拆分时,每个参数扩展为一个单独的单词。当没有位置参数时,"$@"$@扩展为空,即它们会被删除。
?($?)最近(上一个)前台执行的命令的退出状态。
$($$)当前shell进程ID。在子Shell(subshell)中(如命令组合()),它是启动子Shell的Shell进程ID,而不是子Shell的进程ID。
!($!)最近放入后台的作业的进程ID,无论是作为异步命令执行还是使用内置命令bg
-($-)当前的选项,这些选项是在调用时指定的,或是通过set命令指定的,或是shell本身设置的。

调用函数时给它传递参数,在函数中使用位置参数来接收传给函数的参数。示例如下:

#!/bin/bash

# 函数定义
function func() {
    # 函数执行时,变量FUNCNAME的第一个元素被设置为函数的名称
    echo "执行函数的名称:${FUNCNAME[0]}"
    echo "shell脚本的名称:$0"
    echo "传给函数的第一个参数:$1"
    echo "传给函数的第二个参数:$2"
    echo "传给函数的第六个参数:$6"
    # 位置参数由多于1个数字组成时,必须用{}括起来
    echo "传给函数的第十一个参数:${11}"
    # $11为$1与1拼接的字符串,并不是第十一个参数
    echo "\$11为\$1拼接1,不是第十一个参数:$11"
    echo "传给函数的参数个数:$#"
    echo "传给函数的所有参数:$*"
    echo "传给函数的所有参数:$@"
}
# 函数调用,传递参数
func 0 1 2 3 4 5 6 7 8 9 10

执行结果:
4
$*$@会将每个位置参数扩展为单独的单词,会进行单词拆分,"$*"会扩展为一个包含每个参数的单词,每个参数的值由特殊变量IFS的第一个字符分隔,"$@"会将每个位置参数扩展为单独的单词,不会进行单词拆分。示例如下:

#!/bin/bash

# 函数定义
function func() {
    echo "传给函数的参数个数为$#"
    echo "从\$*打印传给函数的每个参数"
    for param in $*
    do
        echo "${param}"
    done

    echo "从\$@打印传给函数的每个参数"
    for param in $@
    do
        echo "${param}"
	done

    echo "从\"\$*\"打印传给函数的每个参数"
    IFS_OLD=${IFS}
    # 将IFS的值修改为,,只以,作为分隔符
    IFS=,
    # "$*"扩展为一个包含每个参数的单词,每个参数的值由,分隔
    for param in "$*"
    do
        echo "${param}"
    done
    # 将IFS恢复为IFS的默认值
    IFS=${IFS_OLD}

    echo "从\"\$@\"打印传给函数的每个参数"
    for param in "$@"
    do
        echo "${param}"
    done
}
# 若位置参数为"Bourne Shell" "BourneAgain Shell" "C Shell"这三个
# $*没在双引号内,每个位置参数都扩展为单独的单词,会进行单词拆分
# 传给函数的参数个数为6
func $*
# 每个位置参数都扩展为单独的单词,$@没在双引号内,会进行单词拆分
# 传给函数的参数个数为6
func $@
# $*在双引号内,会扩展为一个包含每个参数的单词,每个参数的值由特殊变量IFS的第一个字符分隔
# 传给函数的参数个数为1
func "$*"
# 每个位置参数都扩展为单独的单词,$@在双引号内,不会进行单词拆分
# 传给函数的参数个数为3
func "$@"

执行结果:
5

四、local变量

函数的local变量可以用内置命令local声明,这些变量只对函数和它所调用的命令可见,这在shell函数调用其他函数时尤为重要。local命令语法如下:

# local只能在函数中使用。它使得变量名的可见作用域仅限于该函数及其子函数。local命令的选项可以是declare所接受的任何选项
local [option] name[=value]

在函数中不用declare定义的变量默认具有全局属性。示例如下:

#!/bin/bash

function func() {
    # 不用declare定义的变量默认具有全局属性
    var="bash"
}
func
declare -p var
echo ${var}

执行结果:
6
使用local声明的函数的local变量只对该函数和它所调用的命令可见。示例如下:

#!/bin/bash

function func1() {
    # 声明函数func1的local变量,该变量只对函数func1和它所调用的命令可见
    local var="bash"
    echo "在func1中输出的var的值为${var}"
    # 调用函数func2
    func2
}
function func2() {
    echo "在func2中输出的var的值为${var}"
}
# 调用函数func1
func1
declare -p var
echo "在函数外输出的var的值为${var}"

执行结果:
7
如果一个local变量与在前面的作用域中声明的变量同名,则该local变量就是前面的作用域中声明的变量的"影子"变量。在函数中声明的local变量会隐藏同名的global变量,在函数中引用(references)和赋值会引用(refer)local变量,而global变量不会被修改。当函数返回时,global变量再次可见。

在函数外定义了一个变量,如果在函数中修改了该变量的值并且没有声明为它为local变量,则该变量在函数调用时会被修改。示例如下:

#!/bin/bash

var="hello"
function func() {
    # 在函数中修改变量var的值
    var="world"
    echo ${var}
}
echo ${var}
func
echo ${var}

执行结果:
8
函数中的local变量与前一个作用域中的变量同名时,在函数中声明的local变量会隐藏同名的global变量,修改local变量不会改变global变量的值,当函数返回时,global变量再次可见。示例如下:

#!/bin/bash

var="hello"
function func() {
    # 声明函数func的local变量,该变量只对函数func和它所调用的命令可见
    # 该local变量与函数外的变量var同名,会隐藏同名的global变量
    # 在函数中对该local变量赋值不会改变函数外的global变量
    local var="world"
    echo ${var}
}
echo ${var}
func
echo ${var}

执行结果:
9
shell使用动态作用域来控制变量在函数中的可见性。在动态作用域内,可见的变量及其值是导致执行到当前函数的一系列函数调用的结果。一个函数看到的变量的值取决于它在其调用者内的值,如果有的话,不管这个调用者是global作用域还是其他shell函数。这也是一个声明为"影子"的local变量的值,也是函数返回时恢复的值。

变量var1在函数func1中声明为local变量,func1调用了函数func2,变量var2又在函数func2中声明为local变量,func2又调用了函数func3,那么在func2func3var1的值为func1中local变量var1的值,在func3var2的值为func2local变量var2的值,从而掩盖了任何同名的global变量的值。示例如下:

#!/bin/bash

function func1() {
    # 声明local变量var1
    local var1="bash"
    echo "在func1中输出的var1的值为${var1}"
    echo "在func1中输出的var2的值为${var2}"
    # 调用函数func2,func2中的var1的值为func1中local变量var1的值
    # 由于func2中声明了local变量var2,func2中的var2的值为其声明的local变量var2的值
    func2
}
function func2() {
    # 声明local变量var2
    local var2="python"
    echo "在func2中输出的var1的值为${var1}"
    echo "在func2中输出的var2的值为${var2}"
    # 调用函数func3,func3中的var1的值为func1中local变量var1的值
    # func3中的var2的值为func2中local变量var2的值
    func3
}
function func3() {
    echo "在func3中输出的var1的值为${var1}"
    echo "在func3中输出的var2的值为${var2}"
}
var1="csh"
var2="java"
echo "函数func1调用前输出的var1的值为${var1}"
echo "函数func1调用前输出的var2的值为${var2}"
# 调用函数func1,func1中的var2的值为global变量var2的值
# 由于func1中声明了local变量var1,func1中的var1的值为其声明的local变量var1的值
func1
echo "函数func1调用后输出的var1的值为${var1}"
echo "函数func1调用后输出的var2的值为${var2}"

执行结果:
10
内置命令unset也使用相同的动态作用域:如果一个变量是当前作用域的local变量,unset将删除该变量;否则unset将删除任何调用者的作用域中发现的变量。如果一个在当前local作用域的变量被unset,那么它将保持不变,直到它在该作用域中被重置或直到函数返回。一旦函数返回,该变量在前一个作用域中的任何实例将变得可见。如果unset作用于前一个作用域中的变量,那么任何被掩盖的变量实例都变得可见。

使用unset删除函数中的local变量。示例如下:

#!/bin/bash

var1="hello"
var2="sh"
function func1() {
    local var1="world"
    local var2="bash"
    echo "unset前在func1中输出的var1的值为${var1}"
    echo "unset前在func1中输出的var2的值为${var2}"
    # 删除local变量var1,var1是当前作用域的local变量
    unset var1
    # 删除local变量var1后再输出该变量,该变量的值为空
    echo "unset后在func1中输出的var1的值为${var1}"
    # 调用函数func2
    func2
    # func2中使用的unset作用于前一个作用域中的变量即func1的local变量var2,则被掩盖的global变量var2变得可见
    echo "unset后在func1中输出的var2的值为${var2}"
    # 重置local变量var1
    var1="java"
    echo "重置后在func1中输出的var1的值为${var1}"
}
function func2() {
    # 在当前作用域中删除的变量local变量var1将保持不变,直到它在当前作用域被重置或直到函数func1返回
    echo "unset后在func2中输出的var1的值为${var1}"
    # func1调用了func2,func2中的var2的值为func1中local变量var2的值
    echo "unset前在func2中输出的var2的值为${var2}"
    # 删除local变量var2,由于该变量不是当前作用域中的变量,所以删除调用者的作用域中发现的变量
    # 由于func1调用了func2,所以删除的是func1的作用域中的local变量var2
    unset var2
    # func1调用了func2,由于前一个作用域中的变量var2即调用它的函数func1中的local变量var2被删除
    # 则被掩盖的global变量var2变得可见,所以func2中的var2的值为global变量var2的值
    echo "unset后在func2中输出的var2的值为${var2}"
}
# 调用函数func1
func1
# func1返回之后,local变量在前一个作用域中的任何实例将变得可见
echo "函数func1调用后输出的var1的值为${var1}"
echo "函数func1调用后输出的var2的值为${var2}"

执行结果:
11
在函数中使用declare时,如果不使用-g选项,则相当于使用local命令。示例如下:

#!/bin/bash

function func1() {
    # 声明函数func1的local变量var1,该变量只对函数func1和它所调用的命令可见
    local var1="bash"
    # 在函数中使用declare声明变量时没使用-g选项,则相当于使用了local命令
    # 声明函数func1的local变量var2,该变量只对函数func1和它所调用的命令可见
    declare var2="csh"
    echo "在func1中输出的var1的值为${var1}"
    echo "在func1中输出的var2的值为${var2}"
    # 调用函数func2
    func2
}
function func2() {
    echo "在func2中输出的var1的值为${var1}"
    echo "在func2中输出的var2的值为${var2}"
}
# 调用函数func1
func1
declare -p var1 var2
echo "在函数外输出的var1的值为${var1}"
echo "在函数外输出的var2的值为${var2}"

执行结果:
12
declare-g选项赋予变量全局属性,而local命令的选项可以是declare所接受的任何选项,所以local也可以用-g选项赋予变量全局属性。示例如下:

#!/bin/bash

function func() {
    # 声明变量a并赋予全局属性
    declare -g a="hello"
    # 在函数中使用declare声明变量时没使用-g选项,则相当于使用了local命令
    # 声明local变量b
    declare b="world"
    # local命令的选项可以是declare所接受的任何选项
    # 声明变量c并赋予全局属性
    local -g c="bash"
    # 声明local变量d
    local d="csh"
    # 不用declare定义的变量默认具有全局属性
    e="ksh"
}
func
declare -p a b c d e
echo -e "${a}\n${b}\n${c}\n${d}\n${e}"

执行结果:
13
declare-I选项会使local变量继承周围作用域中任何同名变量的属性(nameref属性除外)和值,而local命令的选项可以是declare所接受的任何选项,所以local也可以用-I选项使local变量继承周围作用域中任何同名变量的属性(nameref属性除外)和值。示例如下:

#!/bin/bash

declare -i a=5 b=3 c=2
declare -n ref="b"
function func() {
    # local命令的选项可以是declare所接受的任何选项
    # -I会使local变量继承前一个作用域中任何同名变量的属性(nameref属性除外)和值
    # 这里的local变量a会继承前一个作用域中的变量a的属性和值
    # 这里的local变量b会继承前一个作用域中的变量b的属性和值
    # 这里的local变量ref不会继承前一个作用域中的变量ref的nameref属性,但会继承值
    # 没有现有的变量d,则local变量d初始时是未设置的
    local -I a b ref d
    # 这里的local变量c不会继承前一个作用域中的变量c的属性和值
    local c
    declare -p a b c ref d
    echo "local a : ${a}"
    echo "local b : ${b}"
    echo "local c : ${c}"
    echo "local ref : ${ref}"
    echo "local d : ${d}"
    a=a+1
    c=c+1
    ref=6
    d="hello"
    echo "local a : ${a}"
    echo "local b : ${b}"
    echo "local c : ${c}"
    echo "local ref : ${ref}"
    echo "local d : ${d}"
}
func
# 修改函数中local变量的值不会改变函数外的同名变量的值
echo "a : ${a}"
echo "b : ${b}"
echo "c : ${c}"
echo "ref : ${ref}"

执行结果:
14

五、函数返回

如果在函数中执行了内置命令return,则函数执行结束,并在函数调用后的下一个命令处恢复执行。任何与RETURN陷阱相关联的命令都会在执行恢复之前执行。当函数执行结束时,位置参数和特殊参数#的值会恢复到函数执行前的值。如果return给出一个数值型参数,则该参数就是函数的返回状态,如果没有提供参数,则函数的返回状态就是return之前最后一个被执行的命令的退出状态。如果return提供了一个非数字参数,则函数的返回状态为非0。

执行时,函数的退出状态是函数体中最后执行的命令的退出状态。函数的返回状态是介于0~255的整数。只有0表示成功,其它值都表示失败。return也可以用来终止一个由内置命令.source执行的脚本的执行,返回指定参数或脚本中执行的最后一个命令的退出状态作为脚本的退出状态。如果return提供了一个非数字参数,或者在函数之外使用,而不是在由.source执行脚本期间使用,则返回状态为非0。

在函数中没有用return,函数的退出状态为函数中最后一个被执行的命令的退出状态;在函数中使用return $?,函数的退出状态为return前一个前台执行的命令的退出状态;在函数中使用return但没有提供参数,则函数的退出状态为return之前最后一个被执行的命令的退出状态。示例如下:

#!/bin/bash

function func1() {
    echo "执行函数func1"
    # 该命令退出状态为1
    [[ -z "hello" ]]
}
function func2() {
    echo "执行函数func2"
    # 该命令退出状态为0
    [[ -n "hello" ]]
    return $?
}
function func3() {
    echo "执行函数func3"
    # 执行一个未知命令,会报命令找不到的错误,退出状态为127
    abc
    return
}
func1
# 在函数中没有用return,则函数的退出状态为函数中最后一个被执行的命令的退出状态
echo "函数func1的退出状态为$?"
func2
# 在函数中使用return $?,函数的退出状态(返回状态)为return前一个前台执行的命令的退出状态
echo "函数func2的退出状态为$?"
func3
# 在函数中使用return但没有提供参数,则函数的退出状态(返回状态)为return之前最后一个被执行的命令的退出状态
echo "函数func3的退出状态为$?"

执行结果:
15
在函数中使用return并且给出一个数值型参数,则该参数就是函数的返回状态。示例如下:

#!/bin/bash

function func() {
    # 执行一个未知命令,会报命令找不到的错误
    var=$(abc)
    echo ${var}
    # 返回状态127
    return 127
}
func
if (($?==0))
then
    echo "函数返回状态为0,函数执行成功"
else
    echo "函数返回状态非0,函数执行失败"
fi

执行结果:
16
在函数中使用return并提供了一个非数字参数,则函数的返回状态为非0。示例如下:

#!/bin/bash

function func() {
    echo "执行函数"
    # return提供了一个非数字参数
    return "a"
}
func
# 在函数中使用return并提供了一个非数字参数,函数返回状态为非0
echo "函数的返回状态为$?"

执行结果:
17
return主要用于返回函数的退出状态,不适用于返回函数的处理结果。对于函数的处理结果,可以使用全局属性的变量或在函数内使用echoprintf输出结果,函数外使用命令替换两种方式得到函数处理结果。使用全局属性的变量获取函数处理结果。示例如下:

#!/bin/bash

sum=0
# 该函数用于计算1加到100的和
function func() {
    for (( i=1; i<=100; i++ ))
    do
        ((sum+=i))
    done
}
func
echo "1加到100的和为${sum}"

执行结果:
18
在函数内使用echo输出结果,函数外使用命令替换获取函数处理结果。示例如下:

#!/bin/bash

# 该函数用于计算一个自然数的阶乘
function func() {
    local factorial=1
    for (( i=$1; i>1; i-- ))
    do
        ((factorial*=i))
    done
    echo ${factorial}
}
read -p "输入一个自然数 > " num
# 也可以使用`func num`获取函数的处理结果
# 不使用命令替换而是直接调用函数,会将结果直接输出
echo "${num}的阶乘为$(func num)"

执行结果:
19

六、函数导出

使用内置命令export-f选项可以将函数导出,这样Shell子进程就会自动定义它们。示例如下:

#!/bin/bash

cat > test.sh << EOF
#!/bin/bash

#调用函数func
func
EOF

function func() {
    echo "hello world"
}
# 执行shell脚本test.sh,在test.sh中调用函数func
# 通过bash执行脚本会新启动一个Shell进程执行,该Shell进程是当前Shell进程的Shell子进程
# 函数没有导出,在Shell子进程中没有该函数定义,无法调用
bash test.sh
# 将函数导出,那么Shell子进程中会自动定义该函数
export -f func
# 再次执行shell脚本test.sh,在test.sh中调用函数func成功
bash test.sh

执行结果:
20

七、递归调用

函数可以是递归的,默认情况下,对递归调用的次数没有限制。FUNCNEST变量可用于限制函数调用堆栈的深度,并限制函数调用的次数。如果FUNCNEST变量设置为大于0的数值,则定义函数的最大嵌套级别,超过该限制的函数调用将导致整个命令中止。

函数可以递归调用,默认情况下对递归次数没有限制。示例如下:

#!/bin/bash

factorial=1
# 该函数用于计算一个自然数的阶乘
function func() {
    if (($1>1))
    then
        ((factorial=$1*factorial))
        # 递归调用计算阶乘
        func $(($1-1))
    fi
}
read -p "输入一个自然数 > " num
func num
echo "${num}的阶乘为${factorial}"

执行结果:
21
FUNCNEST变量设置为大于0的数值,可以限制函数调用的次数,超过该限制的函数调用将导致整个命令中止。示例如下:

#!/bin/bash

function func1() {
    echo "执行函数func1"
    func2
}
function func2() {
    echo "执行函数func2"
    func3
}
function func3() {
    echo "执行函数func3"
}
echo "没设置FUNCNEST时函数func1的调用结果"
func1
# 限制函数调用次数为2,超过该限制的函数调用将导致整个命令中止
FUNCNEST=2
echo "设置FUNCNEST的值为2之后函数func1的调用结果"
func1

执行结果:
22
使用FUNCNEST变量也可以限制递归调用的次数。示例如下:

#!/bin/bash

factorial=1
# 该函数用于计算一个自然数的阶乘
function func() {
    if (($1>1))
    then
        ((factorial=$1*factorial))
        # 递归调用计算阶乘
        func $(($1-1))
    fi
}
num=5
# 限制函数调用次数为3,超过该限制的函数调用将导致整个命令中止
FUNCNEST=3
func ${num}
echo "${num}*$((${num}-1))*$((${num}-2))的值为${factorial}"

执行结果:
23

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RtxTitanV

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值