shell函数定义中括号`()`的用途是什么?

Abs*_*cDo 23 bash

一个函数定义为:

do_something () {
    do it
}
Run Code Online (Sandbox Code Playgroud)

我可以理解它的名称“do_something”和大括号来封装操作代码,但我无法理解()这里的目的是什么,因为 Bash 脚本中没有命名参数。将其定义为可能更好更直接

do_something {
  do it
}
Run Code Online (Sandbox Code Playgroud)

这与它当前的语法没有冲突,甚至更多地声明没有命名参数。什么是的用法()吗?

Eli*_*gan 47

如果没有(),语法真的会很模糊。

定义一个函数必须有一些明确的语法,并且在不实质性改变其他shell 语法的情况下,它不可能是这样的:

do_something {
    # one or more commands go here
}
Run Code Online (Sandbox Code Playgroud)

您说这“与其当前的语法不冲突”,但确实如此!请注意,当您尝试运行该文件的第一行时,不会出现任何类型的语法错误。你得到一个错误,但这不是关于语法的错误。带有 的第二行}是语法错误,但第一行不是。相反,do_something {尝试运行一个名为的命令do_something并将其{作为参数传递给该命令:

$ do_something {
do_something: command not found
Run Code Online (Sandbox Code Playgroud)

如果已经有一个名为 的命令do_something,则您正在运行它。如果已经有一个被调用的函数do_something你就是在调用它。一般来说,语法明确很重要,但特别重要的是可以重新定义函数而不会意外调用它。定义一个函数和调用它不应该看起来一样。

外壳如何处理{(.

正如type {你会告诉你的,{是一个shell关键字。这使它像[[. 如果在它本来是命令的情况下使用,则{带有特殊语义。具体来说,它执行命令分组。然而,在其他情况下,它可能被用于未转义的字面{字符。这包括将其作为命令的第二个或后续单词传递的情况。

当然,Bash本来可以被设计成{与目前不同的处理方式。然而,它的语法将不再与 POSIX shell 兼容,并且 Bash 不会真正成为 Bourne 风格的 shell,并且不能运行许多 shell 脚本。

相反,(是一个shell元字符。如果它出现在命令中并且没有被引用(用、 或)' ',它总是被特殊对待。因此,语法中没有歧义:" "\

do_something() {
    # one or more commands go here
}
Run Code Online (Sandbox Code Playgroud)

那不可能是别的意思。如果 Bash 没有函数,那么它将是一个语法错误,因为同样的原因echo foo(bar)是一个语法错误。

如果你真的不喜欢这个()符号,那么你可以使用关键字function并省略它,正如sudodus 提到的那样。请注意,这不是在大多数其他 Bourne 风格的 shell 中定义函数的语法的一部分——在某些情况下,它受到支持,但以这种方式定义的函数具有不同的语义——因此使用它的脚本将不可移植。(这种语法能够明确的原因是它function本身是 Bash 中的一个关键字,表示它后面的任何内容都是函数定义的开始。)

最后,请注意,虽然大多数函数定义{在实践中使用,但允许使用任何复合命令。如果你有一个函数,你希望它的主体总是在子 shell 中运行,你可以使用( )而不是{ }.

  • 在更高的层面上,它关乎人类的期望和可读性。在大多数语言中,特别是在 C、fortran 和其他在 bash 脚本语言开发期间常见的语言中,函数/子例程总是有一个参数列表,可能是空的,用括号括起来。改变这种范式,你就会更难理解这门语言。 (2认同)

sud*_*dus 15

()令牌是告诉shell解释你是声明功能。

$ do_something () { echo 'do it'; } ; do_something
do it
Run Code Online (Sandbox Code Playgroud)

一个替代方案bashfunction

function do_something {
 echo 'do it'
}
Run Code Online (Sandbox Code Playgroud)

或者作为单线你可以测试

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it
Run Code Online (Sandbox Code Playgroud)

  • `function` 关键字是 POSIX 之前的 ksh 主义,bash 支持向后兼容;然而,bash 支持它*糟糕*(没有将变量设为函数本地默认值,就像旧的 ksh 在以该形式声明的函数中所做的那样)。因此,它不适合新代码。请参阅 http://wiki.bash-hackers.org/scripting/obsolete (2认同)

mur*_*uru 9

你真是一个真正的大括号风格的人!

考虑其他完美的大括号样式:

foo
{
  ...
}
foo
  {
    ...
  }
foo
  while ...; do ...; done # Look, ma! No braces!
foo
( ... ) # Look, ma! No braces and a subshell to boot!
Run Code Online (Sandbox Code Playgroud)

shell 如何判断这些是函数定义而不是简单的命令foo后跟命令列表?在所有这些情况下,都需要一个额外的消歧因素,例如 like()function关键字。

  • 我认为有一个论点认为您将其描述为 OTBS 是正确的,因为与 C 和 C++ 中的函数不同,Bourne 风格的 shell 脚本中的函数定义可以*直接* 嵌套在另一个函数定义的主体中,并且所以可以说不值得区分。(或者,这可能是 Java 编程中流行的风格,其中方法将它们的左大括号放在同一行,而不是 OTBS。)无论哪种方式,您都很好地说明了语法如何必须对大括号放置施加严格的约束才能工作根本没有`()`或类似的东西。 (4认同)