foo() { ... }
今天在处理shell脚本时大量使用函数时,我遇到的问题是调用函数中调用的名称相同的变量,即那些变量是相同的。然后我发现可以通过将函数内部的局部变量定义为local:local var = xyz来防止这种情况。
然后,在某些时候,我发现了一个线程(Defining bash function body using parenthesis instead of braces),其中解释了使用括号来定义函数同样有效:
foo() ( ... )
这样做的结果是函数体在子shell中执行,这样做的好处是函数有自己的变量范围,这允许我在没有局部的情况下定义它们。由于具有函数局部范围似乎更有意义,并且比所有变量全局更安全,我立即问自己:
>为什么默认情况下使用括号括起函数体而不是括号?
但是,我很快发现了在子shell中执行该函数的一个主要缺点,特别是从函数内部退出脚本不再起作用,而是迫使我在整个调用树中使用返回状态(如果是嵌套函数)。这引出了我的后续问题:
>使用括号而不是大括号是否还有其他主要缺点(*)(这可能解释为什么大括号似乎更受欢迎)?
(*)我知道(从异常相关的讨论中我偶然发现),有些人认为明确使用错误状态比从任何地方退出要好得多,但我更喜欢后者。
显然这两种风格都有其优点和缺点。所以我希望你们中有些经验丰富的bash用户可以给我一些一般指导:
>我什么时候使用花括号来包围函数体,何时可以切换到括号?
编辑:从答案的外卖
谢谢你的回答,我的头脑现在对此有点清楚了。所以我从答案中得到的是:
>坚持传统的花括号,如果只是为了不混淆脚本的其他潜在用户/开发者(如果整个身体被括在括号中,甚至使用括号)。
>花括号的唯一真正缺点是可以更改父作用域中的任何变量,尽管在某些情况下这可能是一个优点。通过将变量声明为本地变量可以很容易地避免这种情况。
>另一方面,使用括号可能会产生一些严重的不良影响,例如搞乱退出,导致杀死脚本的问题以及隔离变量范围。
Why are braces used by default to enclose the function body instead of parentheses?
函数体可以是任何复合命令。这通常是{list;但是,技术上允许三种其他形式的复合命令:(list),((expression))和[[expression]]。
C语言和C语言中的C语言,如C,Java,C#和JavaScript都使用花括号来分隔函数体。对于熟悉这些语言的程序员来说,大括号是最自然的语法。
Are there other major downsides (*) to using parentheses instead of braces (which might explain why braces seem to be preferred)?
是。子shell中有很多你无法做到的事情,包括:
>更改全局变量。变量更改不会传播到父shell。
>退出脚本。 exit语句将仅退出子shell。
启动子shell也可能是一个严重的性能损失。每次调用该函数时,都会启动一个新进程。
如果您的脚本被杀死,您可能也会遇到奇怪的行为。父子shell接收的信号将发生变化。这是一个微妙的效果,但如果你有陷阱处理程序或你杀死你的脚本那些部分不能按你想要的方式工作。
When shall I use curly braces to enclose the function body,and when is it advisable to switch to parentheses?
我建议你总是使用花括号。如果需要显式子shell,请在花括号内添加一组括号。仅使用括号是非常不寻常的语法,会让许多人阅读您的脚本感到困惑。
foo() { ( subshell commands; ) }