3.3 Shell函数
Shell函数是一种将命令分组以便以后执行的方法,它使用一个单独的名字来分组。当shell函数的名称被用作简单的命令名称时,与该函数名称相关的命令列表就会被执行。 Shell函数在当前的shell上下文中被执行;不会创建新的进程来解释它们。
函数是用这种语法声明的:
fname () compound-command [ redirections ]
或
function fname [()] compound-command [ redirections ]
这定义了一个名为fname的shell函数。保留词function
是可选的。 如果提供了function
保留词,那么括号是可选的。 该函数的主体是复合命令(见5 复合命令)。该命令通常是一个括在{和}之间的list,但也可以是上面列出的任何复合命令。 如果使用了function
保留词,但没有提供括号,建议使用大括号。 只要fname被指定为简单命令的名称,就会执行compound-command。当shell处于POSIX模式时(见6.11 Bash的POSIX模式),fname必须是一个有效的shell名称,并且不能与一个特殊的内置程序相同(见4.4 特殊的内建程序)。 在默认模式下,一个函数名称可以是任何不包含‘$’的未引用的shell单词。任何与shell函数相关的重定向(见3.6 重定向)都会在函数被执行时执行。 一个函数的定义可以用-f选项来删除unset
内置程序(见4.1 Bourne Shell内置程序)。
除非发生语法错误或已经存在一个同名的只读函数,否则函数定义的退出状态为零。 当执行时,函数的退出状态是主体中最后执行的命令的退出状态。
请注意,由于历史原因,在最常见的用法中,包围函数主体的大括号必须用空格
或换行符与主体隔开。 这是因为大括号是保留字,只有当它们与命令列表用空白或其他shell元字符隔开时,才会被识别出来。 另外,在使用大括号时,list必须用分号、‘&’或换行符来结束。
当一个函数被执行时,函数的参数在其执行过程中成为位置参数(见1 位置参数)。 扩展到位置参数数量的特殊参数‘#’被更新以反映这一变化。 特殊参数0
保持不变。 当函数执行时,FUNCNAME
变量的第一个元素被设置为函数的名字。
shell执行环境的所有其他方面在一个函数和它的调用者之间是相同的,除了以下这些例外:DEBUG
和RETURN
陷阱不被继承,除非使用declare
builtin为函数赋予了trace
属性或使用set
内建程序启用了-o functrace
选项(在这种情况下,所有函数都继承DEBUG
和RETURN
陷阱),ERR
陷阱也不被继承,除非-o errtrace
shell选项被启用。参见4.1 Bourne Shell内置程序,以了解对trap
内建程序的描述。
FUNCNEST
变量,如果设置为大于0的数值,则定义了一个最大的函数嵌套级别。函数的调用如果超过限制,就会导致整个命令的终止。
如果在一个函数中执行了内置命令return
,那么该函数就会完成,并在函数调用后的下一条命令中恢复执行。 任何与RETURN
陷阱相关的命令都会在恢复执行前执行。 当一个函数完成时,位置参数和特殊参数‘#’的值会恢复到它们在函数执行前的值。如果给了return
一个数字参数,那就是函数的返回状态;否则,函数的返回状态就是return
之前执行的最后一条命令的退出状态。
函数的局部变量可以用local
内置函数来声明(局部变量)。 通常情况下,变量和它们的值在函数和它的调用者之间共享。 这些变量只对函数和它调用的命令可见。当一个shell函数调用其他函数时,这一点尤其重要。
在下面的描述中,当前作用域是指当前正在执行的函数。之前的作用域包括该函数的调用者,以此类推,一直到"全局"作用域,在这里shell没有执行任何shell函数。 因此,当前局部作用域的局部变量是在当前正在执行的函数中使用local
或declare
内置变量所声明的变量。
局部变量"隐藏"了在先前作用域中声明的同名变量。例如,在一个函数中声明的局部变量隐藏了一个同名的全局变量:引用和赋值了局部变量,而全局变量没有被修改。 当函数返回时,全局变量再次可见。
shell使用动态作用域来控制变量在函数中的可见性。通过动态作用域,可见变量及其值是导致执行到达当前函数的函数调用序列的结果。函数看到的变量的值取决于它在其调用者中的值(如果有的话),无论该调用者是"全局"范围还是另一个shell函数。 这也是局部变量声明"隐藏"的值,也是函数返回时恢复的值。
例如,如果一个变量var
在函数func1
中被声明为局部变量,而func1
调用了另一个函数func2
,那么从func2
中对var
的引用将解析为来自func1
的局部变量var
,隐藏任何名为var
的全局变量。
下面的脚本演示了这一行为。 当执行时,脚本显示
In func2, var = func1 local
func1()
{
local var='func1 local'
func2
}
func2()
{
echo "In func2, var = $var"
}
var=global
func1
unset
内置函数也使用相同的动态作用域:如果一个变量是当前作用域的局部变量,unset
将解除它的设置;否则 unset 将引用在上述任何调用范围中找到的变量。 如果当前局部范围内的变量未设置,它将保持这种状态(显示为未设置),直到它在该作用域中被重置或直到函数返回。一旦函数返回,先前范围内的任何变量实例都将变得可见。如果unset作用于先前作用域中的一个变量, 一旦函数返回,先前范围内的任何变量实例都将变得可见(见下文localvar_unset
shell选项如何改变这种行为)。
函数名称和定义可以用declare
(typeset
)内置命令的-f选项列出(见4.2 Bash的内置命令)。 declare
或typeset
的-F选项将只列出函数名称(如果extdebug
shell选项被激活,还可以选择源文件和行号)。函数可以被导出,这样子shell进程(那些在执行单独的shell调用时创建的进程)就会自动用export
内置的-f选项来定义它们(见4.1 Bourne Shell内置程序)。
函数可以是递归的。 FUNCNEST
变量可以用来限制函数调用堆栈的深度,并限制函数调用的数量。 默认情况下,对递归调用的数量没有限制。