“function foo() {}”和“foo() {}”之间的区别

Car*_*rós 120 shell bash posix function

我可以bash使用或省略function关键字来定义函数。有什么区别吗?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar
Run Code Online (Sandbox Code Playgroud)

这两个函数调用foobar成功,我看不出有什么区别。所以我想知道它是否只是为了提高可读性,或者我遗漏了什么......

顺便说一句,在其他 shell 中,例如dash(在 debian/ubuntu 中/bin/sh符号链接dash)它在使用function关键字时失败。

Gil*_*il' 114

function关键字年推出的KSH。传统的Bourne shell只有foo ()语法,而POSIX只标准化了foo ()语法。

在 ATT ksh(但不是 pdksh)中,由functionBourne/POSIX 语法定义的函数和用 Bourne/POSIX 语法定义的函数之间存在一些差异。在由 定义的函数中functiontypeset关键字声明了一个局部变量:一旦函数退出,变量的值将重置为进入函数之前的值。使用经典语法,无论您是否使用,变量都具有全局作用域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
Run Code Online (Sandbox Code Playgroud)

ksh 的另一个区别是用function关键字定义的函数有自己的陷阱上下文。函数外部定义的陷阱在执行函数时会被忽略,函数内部的致命错误仅退出函数而不是整个脚本。此外,$0是由 定义的函数中的函数名称,function而是由().

Pdksh 不模拟 ATT ksh。在 pdksh 中,typeset无论函数如何,都会创建局部作用域的变量,并且没有局部陷阱(尽管使用function确实会产生一些细微的差异 - 有关详细信息,请参阅手册页)。

Bash 和 zsh 引入了function与 ksh 兼容的关键字。但是,在这些 shell 中function foo { … }foo () { … }严格相同,bash 和 zsh 扩展名也是如此function foo () { … }。该typeset关键字总是声明局部变量(除了-g当然的),和陷阱是不是本地的(你可以通过设置获得的zsh本地陷阱local_traps选项)。

  • 需要注意的是,在 Bourne shell 引入了它的 `foo() 命令`语法之前,函数支持被添加到了 Korn shell 中,为了兼容性,Bourne 语法后来被添加到了 Korn shell 中。 (10认同)

sch*_*iba 54

除了第二个版本更便携这一事实之外,AFAIK 没有区别。

  • 这是一个很大的区别,可移植性......我看到太多的答案根本不适用于(旧的)生产系统,而且还有很多只适用于 linux 的选项。至少,警告这不是最便携的方式......它可能是彻头彻尾的危险(要求某人`tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - "`而不警告他们首先仔细检查 desthost 上的 tar 版本是否会去掉“/”,在某些情况下可能会导致灾难......)。例如:如果使用`function tar { #a safe tar with safety checks ... }` 并且`sh` 忽略它,... (41认同)
  • 没有详细说明它“更便携”的确切程度,答案是没有用的。 (4认同)
  • @OlivierDulac我想补充一点,假设“sh”识别“function”关键字的脚本——并且一般来说,假设“ksh”和“bash”等 shell 提供的常见但非标准功能——通常也无法工作*较新的*生产系统,即使它们适用于同一操作系统的旧版本。`bash` 在许多 GNU/Linux 系统上仍然提供 `sh`,但一些流行的发行版已经转而使用 `sh` 作为 `dash`(Debian Almquist SHell)的符号链接以提高性能。这包括 [Debian](https://wiki.debian.org/DashAsBinSh) 和 [Ubuntu](https://wiki.ubuntu.com/DashAsBinSh)。 (2认同)
  • 如今,业界在 99.9% 以上的环境中使用 bash 或等效的 shell 时,可移植性已不再是争论的焦点。归结为可读性,有两个方面:有人说“功能”使其显而易见,从posix标准或其他shell中学到的人会说大括号是更明显的风格。最后,在您的团队或公司中选择一个并坚持下去。 (2认同)

Sté*_*las 41

foo() any-command
Run Code Online (Sandbox Code Playgroud)

是由支撑在伯恩语法任何类似Bourne壳但是bashyash和的最新版本posh(其仅支持化合物命令)。(除非是复合命令,否则Bourne shell 和 AT&T 实现ksh不支持)。foo() any-command > redirectionsany-command

foo() any-compound-command
Run Code Online (Sandbox Code Playgroud)

(复合命令的例子:{ cmd; }, for i do echo "$i"; done, (cmd)... 最常用的是{ ...; }

是任何类似 Bourne 的 shell 都支持的 POSIX 语法,也是您通常想要使用的语法。

function foo { ...; }
Run Code Online (Sandbox Code Playgroud)

是 Korn shell 语法,它早于 Bourne 语法。只有在专门为 Korn shell 的 AT&T 实现编写并且需要它在那里接受的特定处理时才使用这个。该语法不是 POSIX,但受bash,支持,yash并且zsh为了与 Korn shell 兼容,尽管这些 shell(以及pdkshKorn shell的基于 的变体)不会将其与标准语法区别对待。

function foo () { ...; }
Run Code Online (Sandbox Code Playgroud)

no shell的语法,不应使用。它只是碰巧偶然的支持bashyashzshpdkshKorn shell中的基础变种。顺便说一句,这也是awk函数语法。

如果我们继续沿着深奥的列表,

function foo() other-compound-command
Run Code Online (Sandbox Code Playgroud)

(如function foo() (subshell)function foo() for i do; ... done)甚至更糟。它受bash,yash和支持zsh,但不支持 ksh,甚至是pdksh基于 - 的变体。

尽管:

function foo() simple command
Run Code Online (Sandbox Code Playgroud)

仅支持zsh.

  • Bash 中记录了包含 `function` 关键字和括号的语法。Bash 4.2 及更高版本的手册说函数是通过语法`name ()复合命令[重定向]`或`函数名称[()]复合命令[重定向]`来声明的。在 Bash 4.1.11 至少 3.0-beta 中,这只是单行 `[function] name ()复合命令 [redirection]`,它错误地没有涵盖包含 `function` 关键字但不包括括号的语法,但是仍然涵盖了包含 `function` 关键字和括号的语法。 (3认同)
  • 欢迎来到 2019 年,Linux/Unix 自动化脚本只有一个行业事实上的标准;几乎所有地方都安装了一个解释器(除了奇异的环境):bash。使用所有 bash 功能编写代码,使用 shebang,一切都会好起来的。兼容性的争论是无效的。 (3认同)
  • @StéphaneChazelas 好吧,我想说有一个很好的理由使用 `function f() {`。也就是说,就可读性而言,它会被任何懂英语和任何懂 C 的人认为是一个函数,而不是这些集合中的一个。 (2认同)
  • 应该是公认的答案。非常重要`在定义函数时,永远不要将关键字 function 与括号 () 组合在一起。` (2认同)

dep*_*uid 23

在语义上,这两种形式在 Bash 中是等价的。

从手册页:

Shell 函数声明如下:

name () compound-command [redirection]
function name [()] compound-command [redirection]
Run Code Online (Sandbox Code Playgroud)

这定义了一个名为name. 保留字功能是可选的。如果提供了函数保留字,则括号是可选的。

编辑:我刚刚注意到这个问题被标记为posix。在 POSIX 中shfunction不使用关键字(尽管它被保留)。