在外壳函数定义中,括号()的用途是什么?


20

函数定义为:

do_something () {
    do it
}

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

do_something {
  do it
}

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


Answers:


44

没有(),语法实际上将是模棱两可的。

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

do_something {
    # one or more commands go here
}

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

$ do_something {
do_something: command not found

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

贝壳如何对待{和处理(

就像type {您会说的那样,它{是一个shell关键字。这样就好了[[。如果在原本可能是命令的情况下使用,则{带有特殊的语义。具体来说,它执行命令分组。但是,在其他情况下,可以不转义使用它来表示文字{字符。这包括将其作为命令的第二个或后续单词传递的情况。

当然,Bash 可能被设计为{与当前有所不同。但是,它的语法将不再与POSIX Shell兼容,并且Bash不会真正成为Bourne风格的Shell,并且将无法运行许多Shell脚本。

相反,(是一个外壳元字符。它始终是经过特殊处理的,如果它出现在一个命令,而不是引用(与' '" "\)。因此,语法没有歧义:

do_something() {
    # one or more commands go here
}

那没什么别的了。如果Bash没有函数,那么这将是语法错误,因为同样的原因echo foo(bar)是语法错误。

如果您真的不喜欢这种()表示法,则可以使用关键字function并忽略它,如sudodus所述。请注意,这并不是大多数其他Bourne样式的shell中定义函数的语法的一部分-在某些情况下,它受支持,但是以这种方式定义的函数具有不同的语义 -因此使用该脚本的脚本将不可移植。(此语法能够明确的原因是,function它本身是Bash中的关键字,表示紧随其后的是函数定义的开始。)

最后,请注意,尽管大多数函数定义都{在实践中使用,但允许使用任何复合命令。如果您的函数的主体始终希望在子外壳中运行,则可以使用( )而不是{ }


1
在更高的层次上,这与人类的期望和可读性有关。在大多数语言中,特别是在C,fortran和bash脚本语言开发过程中常见的其他语言中,函数/子例程始终有一个参数列表(可能为空),并用括号括起来。更改该范例,您将很难理解该语言。
jamesqf

15

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

$ do_something () { echo 'do it'; } ; do_something
do it

的替代方法bashfunction

function do_something {
 echo 'do it'
}

或单线测试

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it

2
function关键字是预先POSIX KSH主义该bash的支撑件的向后兼容性; 但是,bash对它的支持很差(不会像在旧版ksh中以该形式声明的函数那样将变量设置为局部函数默认值)。因此,它对于新代码不是理想的。参见wiki.bash-hackers.org/scripting/obsolete
Charles Duffy

@CharlesDuffy,谢谢您的解释:-)
sudodus

1
@CharlesDuffy您是说ksh和bash接受某种语法,使ksh中的变量成为局部变量,而在bash中使变量成为全局变量吗?听起来不对。我的理解部分基于该帖子并进行了检查(在ksh93中),我认为ksh在比bash 更少的情况下使变量成为局部变量,从不超过bash。在bash中,在没有函数的typeset情况下-g,始终声明局部变量。使用function关键字bash和ksh作用域变量的方式相同,不是吗?
卡根

@EliahKagan我认为查尔斯已经提到已经证明吉勒的答案在这里
谢尔盖Kolodyazhnyy

9

您真是个真正的括号风格的主义者!

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

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

Shell如何分辨这些是函数定义,而不仅仅是命令foo和命令列表?在所有这些情况下,都需要一个附加的歧义因素,例如()function关键字。


再次考虑,这实际上不是OTBS,因为OTBS允许在新行上使用括号的地方是函数定义。
muru

3
我觉得有被提出,你是正确的把它定性为OTBS,因为不像在C和C函数++,在Bourne风格的shell脚本函数定义可以嵌套参数直接在另一个函数中定义的身体,所以可以说是不应该被区别对待。(或者这可能是Java编程中流行的样式,其中方法在同一行而不是OTBS上获得开括号。)无论哪种方式,您都很好地指出了语法如何对括号位置施加严格的约束才能起作用根本没有()或类似的东西。
伊莱亚·卡根
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.