当使用bash在脚本中创建函数时,似乎人们经常让函数在子shell中运行,即
function(){(
)}
不是
function(){
}
如果有的话,使用{()}
而不是仅仅使用{}
的好处/缺点是什么?
括号使函数在子shell中运行,子shell是与父shell隔离的子进程。当您希望在不影响函数外代码行为的情况下进行过程范围的环境更改时,它们非常有用。
的例子包括:
-
更改当前目录加
cd
不影响母壳。在子shell中运行cd
是pushd
和popd
的一个更干净的选择。 -
变量赋值都与亚壳层隔离。您可以临时更改全局设置,如
$PATH
和$IFS
,而不必在前后仔细保存和恢复它们的值。 -
Shell选项使用
set
或shopt
更改的内容将在子shell退出时自动恢复。例如,我通常编写(set -x; some-commands)
来临时启用命令日志记录。 -
信号处理程序
trap
只在子shell中有效。您可以在函数运行期间安装自定义INT
(Ctrl-C)处理程序,或者在函数返回时安装自定义EXIT
处理程序来运行清理代码。func() {( echo 'entering func' >&2 trap 'echo exiting func >&2' EXIT ... )}
-
如果
exit
被调用它不会导致整个脚本退出。如果您想从调用堆栈中的几个函数中调用exit
,作为一种穷人的"例外",这是很有用的。或者如果你想要一个可能退出的脚本,将它包装在子shell中可以防止它杀死你的脚本。
( . ./script-that-might-exit echo "script set $foo to $foo" echo "script changed dir to $PWD" )
有趣的事实:函数不必用花括号分隔。省略大括号并使用括号作为分隔符是合法的:
func() (
# runs in a subshell
)
如果在(..)
子shell中调用exit
,它只会终止该表达式。此外,代码可以自由地改变变量的值以及全局选项(通过set
);这些变化在周围的代码中看不到,并且在表达式退出时消失,这可以简化对代码正确性的推理。
当你在函数中使用(...)
时,注意这个陷阱:(...)
中的return
命令不会从函数返回;它将终止(...)
,就像exit
一样。如果在(...)
之后有命令,它们将执行。