Instead of restricting variables with subshells, you can run b as a new process. Then it won't have access to any of the caller's variables, except those you chose to export.
Move function to a new script
If you only need it for a single function, move that whole function into a separate script.
b:
#!/usr/bin/env mksh
set -u -e -o pipefail
echo "in b: VAL= $val"
Output:
in a: VAL= myval
./b[4]: val: parameter not set
Use a wrapper function
If you have lots of functions that need this, then you can move them all into a single file, and include a wrapper function to source-and-execute.
funcs:
#long options not passed on by -$-
set -o pipefail
#execute function without inheriting environment variables
function ni () {
WHERE="${_%/*}"
mksh -$- -c "source $WHERE/funcs ; $*"
}
function a () {
local +x val="myval"
echo "in a: VAL= $val"
ni b
}
function b () {
echo "in b: VAL= $val"
}
This "library" will need to be sourced from the main script:
#!/usr/bin/env mksh
set -e -u -o pipefail
WHERE="${_%/*}"
source $WHERE/funcs
a
This has the disadvantage that you lose the location (script and line number) of the error:
in a: VAL= myval
mksh: val: parameter not set