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


96

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

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

调用函数foobar成功都没有,我看不出有什么区别。所以我想知道这是否只是为了提高可读性,还是我缺少某些东西...

BTW在其他shell中dash(例如/bin/shdash在debian / ubuntu中被符号链接)在使用function关键字时失败。


8
也可以带或不带括号:function baz { echo "baz"; }。见Bashism在GreyCat的wiki。
manatwork 2013年

Answers:


42

除了第二个版本更便于移植之外,AFIAK没有任何区别。


32
这是一个巨大的差异,可移植性...我看到了太多的答案,这些答案根本无法在(较旧的)生产系统上运行,并且还有很多选项仅在linux上有效。至少,警告这不是最可移植的方法...这可能是非常危险的(要求某人在tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " 不警告他们的情况下首先仔细检查desthost上的tar版本是否会摆脱“ /”可能导致灾难在某些情况下...)。例如:如果使用a function tar { #a safe tar with safety checks ... }sh忽略它,...
Olivier Dulac

1
@OlivierDulac我要补充一点的是,假设脚本能够sh识别该function关键字,并且通常假定外壳程序喜欢kshbash提供这些共同但非标准的功能,这些脚本通常也无法在较新的生产系统上运行,即使它们在较旧的版本中也是如此。相同的操作系统。bash仍然sh在许多GNU / Linux系统上提供,但是一些流行的发行版已切换为具有sh符号链接dash(Debian Almquist SHell)以提高性能。这包括DebianUbuntu
伊莱亚·卡根

2
如果不详细说明它的“可移植性”如何确切,答案就没有用。
ivan_pozdeev

如今,在行业中> 99.9%的环境中使用bash或等效shell的情况下,可移植性已不再成为争论。它归结为可读性,它有两个方面:有人说“功能”使它很明显,从posix标准或其他shell中学到的人会说花括号是更明显的样式。最后,在您的小组或公司中选择一个并坚持下去。
Hubert Grzeskowiak

92

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

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

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

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


8
应该注意的是在Bourne shell引入其foo() command语法之前,已将功能支持添加到Korn Shell中,并且为了兼容性,后来将Bourne语法添加到Korn Shell中。
斯特凡Chazelas

2
function { ... }; f;省略关键字f之后是否正确function
Ruslan

32
foo() any-command

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

foo() any-compound-command

(化合物的实例命令:{ cmd; }for i do echo "$i"; done(cmd)...最常用的感{ ...; }

是任何类似Bourne的外壳以及您通常要使用的外壳所支持的POSIX语法。

function foo { ...; }

是Korn shell语法,它早于Bourne语法。仅在专门为Korn shell的AT&T实现编写并且需要在那里接受特定处理的情况下,才使用此代码。该语法不是POSIX,但受POSIX支持bashyashzsh与Korn Shell兼容,尽管这些Shell(以及pdkshKorn Shell 的- 基于-的变体)与标准语法没有任何区别。

function foo () { ...; }

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

如果我们继续往下走,

function foo() other-compound-command

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

而:

function foo() simple command

仅受支持zsh


1
functionBash中记录了同时包含关键字和括号的语法。Bash 4.2及更高版本的手册说,函数是通过语法name () compound-command [ redirections ]或声明的function name [()] compound-command [ redirections ]。在Bash 4.1.11中,直到至少3.0-beta才是单行[ function ] name () compound-command [redirection],它错误地不覆盖包括function关键字但不包含括号的语法,但仍然覆盖同时包含function关键字和括号的语法。
nisetama

@nise的意思是,除了能够与Korn shell兼容(并且始终具有),它还可以bash识别function foo {foo() {因此它可以解释为Korn shell编写的脚本。它也支持function foo () {,但是没有充分的理由使用它。
斯特凡Chazelas

2
@StéphaneChazelas好吧,我想说有充分的理由使用function f() {。也就是说,就可读性而言,只有英语和英语的任何人都可以将它识别为一种功能,而不是其中的一种。
DepressedDaniel

2
应该是公认的答案。真的很重要You should never combine the keyword function with the parentheses () when defining a function.
4wk_

欢迎来到2019,那里只有一个针对Linux / unix自动化脚本的行业实际标准; 几乎到处都安装了一个解释器(不包括异国环境):bash。使用所有bash功能编写代码,使用shebang,一切都会好起来的。兼容性的论点是无效的。
Hubert Grzeskowiak

22

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

从手册页:

Shell函数声明如下:

name () compound-command [redirection]
function name [()] compound-command [redirection]

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

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


3

到目前为止,其他几个人已经正确回答了,但这是我的简要提要:

第二个版本是可移植的,并且可能与许多标准(特别是POSIX)外壳一起使用。

第一个版本仅适用于bash,但您可以省略函数名称后的括号。

否则,在bash解释它们之后,它们代表相同的实体。


实际上,第一个版本除了将其限制为某些shell可接受的语法之外没有其他意义。当您包括() function关键字壳的行为,如果你只是做了foo(){ ...; }反正,除了,当然,换一个外壳中,它是无效的语法。因此,function foo { ...; }如果有必要,您应该这样做,foo(){ ...; }否则。
mikeserv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.