如果函数在子 shell 中,它的行为会如何变化?
How does the behavior of a function change if it is within a subshell?
在脚本中创建函数时 bash,似乎人们经常在子 shell 中使用函数 运行,即
function(){(
)}
而不是
function(){
}
如果有的话,使用 {()}
而不仅仅是 {}
的 benefit/downsides 是什么?
如果在 (..)
子 shell 中调用 exit
,它只会终止该表达式。此外,代码可以自由更改变量的值以及全局选项(通过 set
);这些更改在周围的代码中看不到,并且在表达式退出时消失,这可以简化对代码正确性的推理。
当您在函数内部使用 (...)
时,请注意这个陷阱:(...)
内部的 return
命令不会从函数中 return;它只会终止 (...)
,就像 exit
一样。如果您在 (...)
之后有命令,它们将随后执行。
圆括号导致函数 运行 在子 shell 中,这是一个与父 shell 隔离的子进程。当您想在不影响函数外部代码行为的情况下更改 process-wide 环境时,它们很有用。
示例包括:
将当前目录更改为cd
不会影响父目录shell。 subshell 中的 运行 cd
是 pushd
和 popd
.
的更简洁的替代方案
变量赋值与子shell隔离。您可以临时更改 $PATH
和 $IFS
等全局设置,而无需在前后仔细保存和恢复它们的值。
Shell选项改成set
或shopt
时会自动恢复subshell 退出。例如,我通常写 (set -x; some-commands)
来临时启用命令日志记录。
与trap
一起安装的信号处理程序仅在子shell中有效。您可以在函数运行期间安装自定义 INT
(Ctrl-C) 处理程序,或将自定义 EXIT
处理程序安装到 运行函数 returns.
时的清理代码
func() {(
echo 'entering func' >&2
trap 'echo exiting func >&2' EXIT
...
)}
如果调用 exit
,它不会导致整个脚本退出。如果您想从调用堆栈中的几个函数调用 exit
作为一种穷人的“异常”,这将很有用。
或者,如果您想获取一个可能会退出的脚本,将其包装在一个 subshell 中将防止它杀死您的脚本。
(
. ./script-that-might-exit
echo "script set $foo to $foo"
echo "script changed dir to $PWD"
)
有趣的事实:函数不必用大括号分隔。省略大括号并使用圆括号作为分隔符是合法的:
func() (
# runs in a subshell
)
在脚本中创建函数时 bash,似乎人们经常在子 shell 中使用函数 运行,即
function(){(
)}
而不是
function(){
}
如果有的话,使用 {()}
而不仅仅是 {}
的 benefit/downsides 是什么?
如果在 (..)
子 shell 中调用 exit
,它只会终止该表达式。此外,代码可以自由更改变量的值以及全局选项(通过 set
);这些更改在周围的代码中看不到,并且在表达式退出时消失,这可以简化对代码正确性的推理。
当您在函数内部使用 (...)
时,请注意这个陷阱:(...)
内部的 return
命令不会从函数中 return;它只会终止 (...)
,就像 exit
一样。如果您在 (...)
之后有命令,它们将随后执行。
圆括号导致函数 运行 在子 shell 中,这是一个与父 shell 隔离的子进程。当您想在不影响函数外部代码行为的情况下更改 process-wide 环境时,它们很有用。
示例包括:
将当前目录更改为
的更简洁的替代方案cd
不会影响父目录shell。 subshell 中的 运行cd
是pushd
和popd
.变量赋值与子shell隔离。您可以临时更改
$PATH
和$IFS
等全局设置,而无需在前后仔细保存和恢复它们的值。Shell选项改成
set
或shopt
时会自动恢复subshell 退出。例如,我通常写(set -x; some-commands)
来临时启用命令日志记录。与
时的清理代码trap
一起安装的信号处理程序仅在子shell中有效。您可以在函数运行期间安装自定义INT
(Ctrl-C) 处理程序,或将自定义EXIT
处理程序安装到 运行函数 returns.func() {( echo 'entering func' >&2 trap 'echo exiting func >&2' EXIT ... )}
如果调用
exit
,它不会导致整个脚本退出。如果您想从调用堆栈中的几个函数调用exit
作为一种穷人的“异常”,这将很有用。或者,如果您想获取一个可能会退出的脚本,将其包装在一个 subshell 中将防止它杀死您的脚本。
( . ./script-that-might-exit echo "script set $foo to $foo" echo "script changed dir to $PWD" )
有趣的事实:函数不必用大括号分隔。省略大括号并使用圆括号作为分隔符是合法的:
func() (
# runs in a subshell
)