bash 函数:将正文括在大括号与括号中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27801932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
bash functions: enclosing the body in braces vs. parentheses
提问by flotzilla
Usually, bash functions are defined using curly braces to enclose the body:
通常,bash 函数使用花括号来定义主体:
foo()
{
...
}
When working on a shell script today making extensive use of functions, I've run into problems with variables that have the same name in the called as in the calling function, namely that those variables are the same. I've then found out that this can be prevented by defining the local variables inside the function as local: local var=xyz
.
今天在处理大量使用函数的 shell 脚本时,我遇到了在被调用函数中与在调用函数中具有相同名称的变量的问题,即这些变量是相同的。然后我发现这可以通过将函数内的局部变量定义为 local: 来防止local var=xyz
。
Then, at some point, I've discovered a thread (Defining bash function body using parenthesis instead of braces) in which it is explained that it's just as valid to define a function using parentheses like this:
然后,在某个时候,我发现了一个线程(使用括号而不是大括号定义 bash 函数体),其中解释说使用这样的括号定义函数同样有效:
foo()
(
...
)
The effect of this is that the function body is executed in a subshell, which has the benefit that the function has its own variable scope, which allows me to define them without local. Since having a function-local scope seems to make much more sense and to be much safer than all variables being global, I immediately ask myself:
这样做的效果是函数体是在子shell中执行的,这样做的好处是函数有自己的变量范围,这允许我在没有本地的情况下定义它们。由于具有函数局部作用域似乎比所有变量都是全局变量更有意义并且更安全,因此我立即问自己:
- Why are braces used by default to enclose the function body instead of parentheses?
- 为什么默认情况下使用大括号而不是括号来包围函数体?
However, I quickly also discovered a major downside to executing the function in a subshell, specifically that exiting the script from inside a function doesn't work anymore, instead forcing me to work with the return status along the whole call tree (in case of nested functions). This leads me to this follow-up question:
但是,我很快也发现了在子 shell 中执行函数的一个主要缺点,特别是从函数内部退出脚本不再起作用,而是迫使我沿着整个调用树处理返回状态(以防万一嵌套函数)。这使我想到了这个后续问题:
- Are there other major downsides (*) to using parentheses instead of braces (which might explain why braces seem to be preferred)?
- 使用括号代替大括号是否还有其他主要缺点 (*)(这可能解释了为什么大括号似乎更受欢迎)?
(*) I'm aware (from exception-related discussions I've stumbled upon over time) that some would argue that explicitly using the error status is much better than being able to exit from anywhere, but I prefer the latter.
(*) 我知道(从我随着时间的推移偶然发现的与异常相关的讨论中)有些人会争辩说明确使用错误状态比能够从任何地方退出要好得多,但我更喜欢后者。
Apparently both styles have their advantages and disadvantages. So I hope some of you more experienced bash users can give me some general guidance:
显然,这两种风格都有其优点和缺点。所以我希望你们中一些更有经验的 bash 用户可以给我一些一般性的指导:
- When shall I use curly braces to enclose the function body, and when is it advisable to switch to parentheses?
- 什么时候应该用花括号把函数体括起来,什么时候改用括号呢?
EDIT: Take-aways from the answers
编辑:从答案中提取
Thanks for your answers, my head's now a bit clearer with regards to this. So what I take away from the answers is:
感谢您的回答,我的头脑现在对此更加清楚了。所以我从答案中得出的结论是:
Stick to the conventional curly braces, if only in order not to confuse potential other users/developers of the script (and even use the braces if the whole body is wrapped in parentheses).
The only real disadvantage of the curly braces is that any variable in the parent scope can be changed, although in some situations this might be an advantage. This can easily be circumvented by declaring the variables as
local
.Using parentheses, on the other hand, might have some serious unwanted effects, such as messing up exits, leading to problems with killing a script, and isolating the variable scope.
坚持使用传统的大括号,只是为了不混淆脚本的其他潜在用户/开发人员(如果整个主体都用括号括起来,甚至使用大括号)。
花括号唯一真正的缺点是父作用域中的任何变量都可以更改,尽管在某些情况下这可能是一个优点。这可以通过将变量声明为 来轻松规避
local
。另一方面,使用括号可能会产生一些严重的不良影响,例如弄乱退出、导致杀死脚本的问题以及隔离变量作用域。
采纳答案by John Kugelman
Why are braces used by default to enclose the function body instead of parentheses?
为什么默认情况下使用大括号而不是括号来包围函数体?
The body of a function can be any compound command. This is typically { list; }
, but three other forms of compound commands are technically allowed: (list)
, ((expression))
, and [[ expression ]]
.
函数体可以是任何复合命令。这通常是{ list; }
,但其它三种形式化合物命令被允许技术上:(list)
,((expression))
,和[[ expression ]]
。
C and languages in the C family like C++, Java, C#, and JavaScript all use curly braces to delimit function bodies. Curly braces are the most natural syntax for programmers familiar with those languages.
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)?
使用括号代替大括号是否还有其他主要缺点 (*)(这可能解释了为什么大括号似乎更受欢迎)?
Yes. There are numerous things you can't do from a sub-shell, including:
是的。有很多事情你不能从子 shell 做,包括:
- Change global variables. Variables changes will not propagate to the parent shell.
- Exit the script. An
exit
statement will exit only the sub-shell.
- 更改全局变量。变量更改不会传播到父 shell。
- 退出脚本。一条
exit
语句将仅退出子 shell。
Starting a sub-shell can also be a serious performance hit. You're launching a new process each time you call the function.
启动子 shell 也会严重影响性能。每次调用该函数时,都会启动一个新进程。
You might also get weird behavior if your script is killed. The signals the parent and child shells receive will change. It's a subtle effect but if you have trap
handlers or you kill
your script those parts not work the way you want.
如果您的脚本被杀死,您也可能会出现奇怪的行为。父壳和子壳接收到的信号会发生变化。这是一个微妙的效果,但是如果您有trap
处理程序或您kill
的脚本,这些部分就无法按您想要的方式工作。
When shall I use curly braces to enclose the function body, and when is it advisable to switch to parentheses?
什么时候应该用花括号把函数体括起来,什么时候改用括号呢?
I would advise you to always use curly braces. If you want an explicit sub-shell, then add a set of parentheses inside the curly braces. Using just parentheses is highly unusual syntax and would confuse many people reading your script.
我建议你总是使用花括号。如果你想要一个显式的子外壳,那么在花括号内添加一组括号。只使用括号是非常不寻常的语法,会让很多阅读你的脚本的人感到困惑。
foo() {
(
subshell commands;
)
}
回答by fedorqui 'SO stop harming'
It really matters. Since bash functions do not return values and the variables they used are from the global scope (that is, they can access the variables from "outside" its scope), the usual way to handle the output of a function is to store the value in a variable and then call it.
这真的很重要。由于 bash 函数不返回值并且它们使用的变量来自全局作用域(也就是说,它们可以从其作用域“外部”访问变量),因此处理函数输出的常用方法是将值存储在一个变量,然后调用它。
When you define a function with ()
, you are right: it will create sub-shell. That sub-shell will contain the same values the original had, but won't be able to modify them. So that you are losing that resource of changing global scope variables.
当你用 定义一个函数时()
,你是对的:它会创建子外壳。该子 shell 将包含与原始值相同的值,但将无法修改它们。这样你就失去了改变全局范围变量的资源。
See an example:
看一个例子:
$ cat a.sh
#!/bin/bash
func_braces() { #function with curly braces
echo "in $FUNCNAME. the value of v=$v"
v=4
}
func_parentheses() (
echo "in $FUNCNAME. the value of v=$v"
v=8
)
v=1
echo "v=$v. Let's start"
func_braces
echo "Value after func_braces is: v=$v"
func_parentheses
echo "Value after func_parentheses is: v=$v"
Let's execute it:
让我们执行它:
$ ./a.sh
v=1. Let's start
in func_braces. the value of v=1
Value after func_braces is: v=4
in func_parentheses. the value of v=4
Value after func_parentheses is: v=4 # the value did not change in the main shell
回答by kojiro
I tend to use a subshell when I want to change directories, but always from the same original directory, and cannot be bothered to use pushd/popd
or manage the directories myself.
当我想更改目录时,我倾向于使用子shell,但总是来自同一个原始目录,并且不会打扰自己使用pushd/popd
或管理目录。
for d in */; do
( cd "$d" && dosomething )
done
This would work as well from a function body, but even if you definethe function with curly braces, it is still possible to use it from a subshell.
这也适用于函数体,但即使您使用花括号定义函数,仍然可以从子shell 中使用它。
doit() {
cd "" && dosomething
}
for d in */; do
( doit "$d" )
done
Of course, you can still maintain variable scope inside a curly-brace-defined function using declare or local:
当然,您仍然可以使用declare 或local 在花括号定义的函数内维护变量范围:
myfun() {
local x=123
}
So I would say, explicitly define your function as a subshell only if notbeing a subshell is detrimental to the obvious correct behavior of that function.
所以我会说,只有当不是子shell 不利于该函数的明显正确行为时,才将您的函数明确定义为子shell 。
Trivia: As a side note, consider that bash actually alwaystreats the function as a curly-brace compound command. It just sometimes has parentheses in it:
琐事:作为旁注,请考虑 bash 实际上始终将函数视为花括号复合命令。它只是有时会有括号:
$ f() ( echo hi )
$ type f
f is a function
f ()
{
( echo hi )
}
回答by Pasha Orehov
Note: Sometimes braced list runs not in same process:
注意:有时花括号列表在不同的进程中运行:
a=22; { echo $a; a=46; echo $a; }; echo $a
says 22 46 46
but
但
a=22; { echo $a; a=46; echo $a; }|cat; echo $a
says 22 46 22
Thanks to fedorqui :)
感谢 fedorqui :)