我可以bash
使用或省略function
关键字来定义函数。有什么区别吗?
#!/bin/bash
function foo() {
echo "foo"
}
bar() {
echo "bar"
}
foo
bar
对函数的调用都foo
成功bar
了,我看不出有什么区别。所以我想知道这是否只是为了提高可读性,或者我缺少什么......
顺便说一句,在其他 shell 中,例如dash
(在 debian/ubuntu 中/bin/sh
符号链接dash
),使用关键字时会失败function
。
答案1
存在两种不同语法的原因是历史性的。关键词function
来自克什。受 C 启发的()
语法来自伯恩外壳。POSIX仅标准化 Bournefoo ()
语法。 Bash 和 zsh 支持两者,以及混合function foo () { … }
.除了 ATT ksh 之外,生成的函数完全相同。
请注意语法中的陷阱()
:函数名称受别名扩展的影响。
alias f=g
f () { echo foo; }
type f # f is an alias for g
type g # g is a shell function
f # alias f → function g → print foo
\f # no alias lookup → f: not found
g # function g
function
在 ATT ksh(但不是 pdksh 及其后代,例如 mksh)中,由 Bourne/POSIX 语法定义的函数和用 Bourne/POSIX 语法定义的函数之间存在一些差异。在由 定义的函数中function
,该typeset
关键字声明一个局部变量:一旦函数退出,该变量的值将重置为进入该函数之前的值。使用经典语法,无论您使用typeset
与否,变量都具有全局作用域。
$ ksh -c 'a=global; f () { typeset a=local; }; f; echo $a'
local
$ ksh -c 'a=global; function f { typeset a=local; }; f; echo $a'
global
ksh 中的另一个区别是使用function
关键字定义的函数有自己的陷阱上下文。执行函数时,函数外部定义的陷阱将被忽略,函数内部的致命错误仅退出该函数,而不退出整个脚本。另外,$0
是由 定义的函数中的函数名称,function
但是由 定义的函数中的脚本名称()
。
Pdksh 不模拟 ATT ksh。在 pdksh 中,typeset
无论函数如何,都会创建本地范围的变量,并且没有本地陷阱(尽管使用function
确实会产生一些细微的差异 - 有关详细信息,请参阅手册页)。
Bash 和 zsh 引入了function
与 ksh 兼容的关键字。然而,在这些 shell 中function foo { … }
, 和foo () { … }
是严格相同的,bash 和 zsh 扩展也是如此function foo () { … }
(解析定义时潜在的别名扩展除外,如上所述)。关键字typeset
总是声明局部变量(-g
当然除了 with ),陷阱不是局部的(你可以通过设置选项在 zsh 中获得局部陷阱local_traps
)。
答案2
据我所知,除了第二个版本更便携之外,没有什么区别。
答案3
foo() any-command
是任何类似 Bourne 的 shell 支持的 Bourne 语法,但是bash
,yash
和最新版本posh
(仅支持复合命令)。 (Bourne shell 和 AT&T 实现ksh
不支持foo() any-command > redirections
除非any-command
是复合命令)。
foo() any-compound-command
(复合命令的示例:{ cmd; }
, for i do echo "$i"; done
, (cmd)
... 最常用的是{ ...; }
)
是任何类似 Bourne 的 shell 都支持的 POSIX 语法,也是您通常想要使用的语法。
function foo { ...; }
是 Korn shell 语法,它早于 Bourne 语法。仅当专门为 Korn shell 的 AT&T 实现编写并且需要它在那里接受的特定处理时才使用此选项。该语法不是 POSIX,但受bash
、yash
和zsh
Korn shell 支持,尽管这些 shell(以及pdksh
Korn shell 的基于 - 的变体)不会将其视为与标准语法有任何不同。
function foo () { ...; }
的语法是不壳和不应该使用。它只是偶然被bash
、yash
和Korn shell 的基础变体zsh
支持。pdksh
顺便说一句,这也是awk
函数语法。
如果我们继续深入深奥的清单,
function foo() other-compound-command
(如function foo() (subshell)
或function foo() for i do; ... done
)更糟糕。bash
、yash
和都支持它zsh
,但 ksh 不支持它,甚至pdksh
基于 - 的变体也是如此。
尽管:
function foo() simple command
仅支持zsh
.
foo bar() any-command
甚至:
$function_list() any-command
一次定义多个函数仅支持zsh
function { body; } args
() any-compound-command args
哪个是匿名函数调用,仅支持zsh
as well。
答案4
到目前为止,其他几个人已经正确回答了,但这是我的简要概要:
第二个版本是可移植的,并且可能与许多标准(特别是 POSIX)shell 一起使用。
第一个版本仅适用于 bash,但您可以省略函数名称后面的括号。
否则,在 bash 解释它们之后,它们代表相同的实体。