bash函数:用大括号括住括号
通常,使用大括号来定义bash函数来封装正文:
foo()
{
...
}
在今天对shell脚本进行大量使用函数的工作时,我遇到了与调用函数中的名称相同的变量的问题,即这些变量是相同的。 然后我发现可以通过在函数内定义局部变量local: local var=xyz
来防止这种情况。
然后,在某个时候,我发现了一个线程(使用括号而不是花括号定义bash函数体),其中解释了使用括号来定义函数同样有效:
foo()
(
...
)
这样做的效果是函数体是在一个子shell中执行的,这有一个好处,即该函数具有自己的变量作用域,这允许我在没有本地的情况下定义它们。 由于拥有一个函数局部范围似乎更有意义,并且比所有变量全局更安全,我立刻问自己:
然而,我很快发现了在子shell中执行函数的一个主要缺点,特别是从函数内部退出脚本不再工作,而是迫使我在整个调用树中处理返回状态(在嵌套函数)。 这导致我出现了这个后续问题:
(*)我知道(从与异常相关的讨论中我发现随着时间的推移)有些人会认为明确地使用错误状态比能够从任何地方退出要好得多,但我更喜欢后者。
显然,这两种风格都有其优点和缺点。 所以我希望你们中一些更有经验的bash用户可以给我一些一般的指导:
编辑:采取 - 答案
感谢您的回答,我的头现在更清晰一些。 所以我从答案中拿出的是:
坚持传统的花括号,只是为了不混淆潜在的其他用户/脚本的开发人员(甚至如果整个身体被括在括号中使用括号)。
花括号唯一真正的缺点是可以改变父范围中的任何变量,尽管在某些情况下这可能是一个优点。 通过将变量声明为local
可以很容易地避免这种情况。
另一方面,使用括号可能会产生一些严重的不良影响,例如搞乱退出,导致杀死脚本和隔离变量范围等问题。
为什么默认使用花括号括住函数体而不是括号?
函数的主体可以是任何复合命令。 这通常是{ list; }
{ list; }
,但在技术上允许三种其他形式的复合命令: (list)
, ((expression))
和[[ expression ]]
。
C语言和C ++语言,如C ++,Java,C#和JavaScript都使用大括号来分隔函数体。 花括号是熟悉这些语言的程序员最自然的语法。
是否还有其他主要缺点(*)使用圆括号代替大括号(这可能解释为什么大括号似乎是首选)?
是。 你无法从一个子shell中做很多事情,包括:
exit
语句只会退出子shell。 启动一个子shell也可能是一个严重的性能问题。 每次调用函数时都会启动一个新进程。
如果您的脚本被杀害,您也可能会产生奇怪的行为。 父母和小孩的信号会发生变化。 这是一个微妙的效果,但如果你有trap
处理程序或你kill
你的脚本,那些部分不能按你想要的方式工作。
何时应使用大括号将函数体封闭,何时建议切换到括号?
我建议你总是使用花括号。 如果你想要一个明确的子shell,那么在花括号里添加一组圆括号。 使用括号是非常不寻常的语法,会混淆许多人阅读你的脚本。
foo() {
(
subshell commands;
)
}
当我想更改目录时,我倾向于使用子shell,但始终使用相同的原始目录,并且无法使用pushd/popd
或自己管理目录。
for d in */; do
( cd "$d" && dosomething )
done
这同样适用于函数体,但即使用花括号定义了函数,仍然可以从子shell中使用它。
doit() {
cd "$1" && dosomething
}
for d in */; do
( doit "$d" )
done
当然,你仍然可以使用declare或local来维护一个花括号定义的函数中的变量作用域:
myfun() {
local x=123
}
所以我会说,明确地定义你的函数作为一个子shell,只是如果不是一个子shell是不利于该功能明显的正确行为。
琐事:作为一个侧面提示,考虑到bash实际上总是将该函数视为一个花括号复合命令。 它有时只有一个括号:
$ f() ( echo hi )
$ type f
f is a function
f ()
{
( echo hi )
}
这真的很重要。 由于bash函数不返回值,并且它们使用的变量来自全局作用域(即,它们可以从其作用域的“外部”访问变量),所以处理函数输出的常用方法是将值存储在一个变量然后调用它。
当你用()
定义一个函数时,你是对的:它会创建子shell。 该子shell将包含原始值相同的值,但无法修改它们。 这样你就失去了改变全局范围变量的资源。
看一个例子:
$ 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"
让我们执行它:
$ ./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
链接地址: http://www.djcxy.com/p/97063.html
上一篇: bash functions: enclosing the body in braces vs. parentheses
下一篇: bash command groups: Why do curly braces require a semicolon?