When making functions within a script it bash, it appears that people often have the function run within a subshell, ie
function(){(
)}
instead of
function(){
}
What are the benefit/downsides of using {()}
rather than just {}
if any?
CodePudding user response:
Parentheses cause the function to run in a subshell, which is a child process isolated from the parent shell. They're useful when you want to make process-wide environmental changes without affecting the behavior of code outside of the function.
Examples include:
Changing the current directory with
cd
does not affect the parent shell. Runningcd
in a subshell is a cleaner alternative topushd
andpopd
.Variable assignments are isolated to the subshell. You can temporarily change global settings like
$PATH
and$IFS
without having to carefully save and restore their values before and after.Shell options changed with
set
orshopt
will be automatically restored when the subshell exits. I commonly write(set -x; some-commands)
to temporarily enable command logging, for example.Signal handlers installed with
trap
are only in effect in the subshell. You can install a customINT
(Ctrl-C) handler for the duration of a function, or a customEXIT
handler to run cleanup code when the function returns.func() {( echo 'entering func' >&2 trap 'echo exiting func >&2' EXIT ... )}
If
exit
is called it won't cause the entire script to exit. This is useful if you want to callexit
from several functions down the call stack as a sort of poor man's "exception".Or if you want to source a script that might exit, wrapping it in a subshell will keep it from killing your script.
( . ./script-that-might-exit echo "script set \$foo to $foo" echo "script changed dir to $PWD" )
Fun fact: Functions don't have to be delimited by curly braces. It's legal to omit the braces and use parentheses as the delimiters:
func() (
# runs in a subshell
)
CodePudding user response:
If exit
is called in a (..)
subshell, it will only terminate that expression. Moreover, the code is free to change the values of variables as well as global options (via set
); those changes are not seen in the surrounding code and are gone when the expression exits, which can simplify reasoning about the correctness of the code.
When you use (...)
inside a function, watch out of this pitfall: a return
command inside the (...)
won't return from the function; it will just terminate the (...)
, just like exit
. If you have commands after the (...)
, they will then execute.