I made a small experiment to save in one line some context.
It works exactly as I wanted. So this post is: 1. to share it with the community. 2. to improve it, or have a complete other solution because it is ugly and cryptic.
The situation is the following : there is a global variable and I want locally in functions to be able to change it and restore it at the end:
x()
{
typeset loc=$glob
glob=<val>
(...)
glob=$loc
}
This is too verbose for me, and asking for typos.
So I tried this (code ready to use):
#!/bin/bash
glob=1
indent=0
# appliance
trace()
{
printf "%$((${indent}*3))s %s\n" "" "$1"
}
# the context stuff (creation and destruction)
# in variable, because with a function, I'd need to create a $(sub shell) and it wouldn't work
new_glob='trap restore_context RETURN;typeset loc=$glob;glob'
restore_context()
{
res=$?
glob=$loc
trap - RETURN
}
# common test stuff, to isolate the traces
test_call()
{
typeset res
trace "in ${FUNCNAME[1]}, before $1,glob=$glob"
(( indent++ ))
eval $1
res=$?
(( indent-- ))
trace "in ${FUNCNAME[1]}, res of $1=$res"
trace "in ${FUNCNAME[1]}, after $1,glob=$glob"
return $res
}
# Russian dolls function
f()
{
eval "$new_glob=6"
test_call g
return 16
}
g()
{
eval "$new_glob=7"
test_call h
return 17
}
h()
{
eval "$new_glob=8"
trace "in h, glob=$glob"
i
return 18
}
i()
{
trace "in i, glob=$glob"
}
# main
test_call f
Here the script calls f, which calls g, which call h. Each function changes the global variable and then restore it.
Output:
# ./test_rtrap
in main, before f,glob=1
in f, before g,glob=6
in g, before h,glob=7
in h, glob=8
in i, glob=8
in g, res of h=18
in g, after h,glob=7
in f, res of g=17
in f, after g,glob=6
in main, res of f=16
in main, after f,glob=1
The important point here is that function i() prints 8, and not 1, as using local variables would do.
To reach this result, I use a trap function on RETURN.
Now the above function x simply becomes:
x()
{
eval "$new_glob=6"
(...)
}
That's a pity that I had to use an eval and a variable containing (part of) code. It is not natural, quite cryptic. But I needed it because using a function there would have required a subshell, with the related variables context issues.
So, not perfect, not very beautiful, but less verbose, and it works.
Is there a better way to perform this, than the ugly eval "$new_glob=6"