shell脚本系列:4、shell函数
文章目录
Shell函数是一种将命令分组的方法,以便稍后使用组的单个名称执行。它们就像“常规”命令一样被执行。当使用shell函数名作为简单的命令名时,将执行与该函数名相关的命令列表。Shell函数在当前Shell上下文中执行;没有创建新的进程来解释它们。
1. 函数语法
函数的声明语法如下:
或者(在有的时候不带function解释器识别不了,所以一般还是加上好一些):
这定义了一个名为fname的shell函数。保留字功能是可选的。如果提供了函数保留字,括号是可选的。函数的主体是复合命令compound-command(参见复合命令)。该命令通常是一个包含在{和}之间的列表,但也可以是上面列出的任何复合命令,但有一个例外:如果使用了函数保留字,但没有提供括号,则需要使用大括号。只要指定fname为命令名,就执行复合命令。当shell处于POSIX模式时(请参阅Bash POSIX模式),fname必须是一个有效的shell名称,并且可能与特殊内置(请参阅特殊内置)不同。在默认模式下,函数名可以是任何不包含$的不带引号的shell字。与shell函数相关的任何重定向(请参阅重定向)都将在执行该函数时执行。可以使用-f选项删除未设置的内置函数(参见Bourne Shell内置函数)。
函数定义的退出状态为零,除非出现语法错误或已存在同名的只读函数。当执行时,函数的退出状态是函数体中最后执行的命令的退出状态。
2. 注意事项
注意,由于历史原因,在最常见的用法中,包围函数体的花括号必须用空格或换行符与函数体分隔。这是因为大括号是保留字,只有当它们以空格或另一个shell元字符与命令列表分隔时才被识别为保留字。此外,当使用大括号时,列表必须以分号、’ & '或换行符结束。
当一个函数被执行时,函数的参数在其执行期间成为位置参数(参见位置参数)。扩展到位置参数数量的特殊参数’ # '将被更新以反映更改。特殊参数0不变。在执行函数时,FUNCNAME
变量的第一个元素被设置为函数的名称。
shell执行环境的所有其他方面在函数和它的调用者之间是相同的,除了这些例外:DEBUG
和RETURN
陷阱并非继承,除非函数提供trace
属性、使用declare
内置 或- o functrace
选项被启用set
内置(在这种情况下,继承了所有功能DEBUG
和RETURN
陷阱),ERR
陷阱并不继承,除非- o errtrace
shell选项已启用。请参阅Bourne Shell Builtins,以获得对内置陷阱的描述。
如果FUNCNEST
变量被设置为一个大于0的数值,那么它将定义一个最大的函数嵌套级别。超过限制的函数调用将导致整个命令中止。
如果在函数中执行内置命令return
,则函数完成并在调用函数后继续执行下一个命令。在恢复执行之前,与RETURN
缺陷相关的任何命令都会被执行。当函数完成时,位置形参和特殊形参’ # '的值将恢复为函数执行前的值。如果返回一个数值参数,那就是函数的返回状态;否则函数的返回状态是返回之前执行的最后一个命令的退出状态。
3. 函数变量作用域
函数的局部变量可以用local
内置函数声明。这些变量只对函数及其调用的命令可见。当一个shell函数调用其他函数时,这一点尤其重要。
局部变量会“遮蔽”在前面作用域中声明的同名变量。例如,函数中声明的局部变量隐藏同名的全局变量:引用和赋值引用局部变量,而不修改全局变量。当函数返回时,全局变量再次可见。
shell使用动态范围来控制变量在函数中的可见性。使用动态作用域,可见变量及其值是导致执行到达当前函数的函数调用序列的结果。函数看到的变量的值取决于它在调用者中的值(如果有的话),该调用者是“全局”作用域还是另一个shell函数。这也是局部变量声明“阴影”的值,也是函数返回时恢复的值。
例如,如果一个变量var在函数func1中声明为局部变量,而func1调用另一个函数func2,在func2中对var的引用将解析为func1中的局部变量var,从而遮蔽任何名为var的全局变量。
下面的脚本演示了这种行为。执行时,将显示:
unset
内置变量也使用相同的动态作用域:如果一个变量是当前作用域的局部变量,unset
将取消它的设置;否则unset将引用上述任何调用范围内的变量。如果当前局部作用域的变量未被设置,它将一直保持此状态,直到在该作用域中被重置或函数返回为止。一旦函数返回,该变量在前一个作用域的任何实例都将可见。如果unset作用于前一个作用域的变量,则带有该名称的变量的任何实例将变为可见。
4. 其它
函数名和定义可以在declare
(typeset
)内置命令(参见Bash Builtins)中用-f选项列出。用于declare
或typeset
的-F
选项将只列出函数名(如果启用了extdebug
shell选项,还可以选择列出源文件和行号)。函数可以被导出,以便子Shell自动将它们用-f
选项定义到导出内置(参见Bourne Shell内置)。
函数可以是递归的。FUNCNEST
变量可以用来限制函数调用堆栈的深度和函数调用的数量。默认情况下,没有对递归调用的数量进行限制。