Next: , Previous: , Up: overview   [Contents][Index]


1.5 Result variables

Some functions have a _var variant; for example, two functions exists to extract the filename extension:

mbfl_file_extension
mbfl_file_extension_var

the first variant will print the result to stdout, while the _var variant will store the result in a “result variable” whose name is handed to it as first argument; this mechanism is built using the local -n feature of Bash. Example:

mbfl_file_extension /path/to/file.ext
-| ext

local _RV
mbfl_file_extension_var _RV /path/to/file.ext
"$_RV"  ⇒ ext

The _var variants are often a bit faster because the output variant is often a wrapper for the _var variant.

Dangers of result variables

We must be careful when using this feature! Let’s consider this script:

function main () {
    local -i X=0
    mbfl_func X
    echo X=$X >&2
}
function mbfl_func () {
    local -n Y=${1:?}
    Y=1
}

main

everything is fine: the script will print X=1 because the call to mbfl_func will mutate the variable X in its execution environment, and such variable happens to be defined in the scope of main.

Now let’s consider this script:

function main () {
    local -i X=0
    mbfl_func X
    echo X=$X >&2
}
function mbfl_func () {
    local -n Y=${1:?}
    local X
    Y=1
}

main

it will print X=0 because mbfl_func accesses the variable X in its execution environment, and such variable is defined by mbfl_func itself; the local definition of X shadows the upper level definition.

There is no true escape from this problem! There is no definitive way to avoid “fishing” a local variable in a lower function from an upper function. MBFL attempts to mitigate the problem by prefixing its variables with mbfl_ when a function uses reference variables. We should also prefix our “result variables”, for example with a leading or trailing underscore: MBFL never uses such a prefix or suffix.

So we should write the script as follows:

function main () {
    local -i _X=0
    mbfl_func _X
    echo _X=$_X >&2
}
function mbfl_func () {
    local -n mbfl_Y=${1:?}
    local mbfl_X
    mbfl_Y=1
}

main

Using the preprocessor

MBFL’s preprocessor has facilities to help us create variables with unique names that we can safely use as argument to functions, Using the script preprocessor.

With these facilities, the first example should be written as:

mbfl_local_varref(RV)

mbfl_file_extension_var mbfl_datavar(RV) /path/to/file.ext
"$RV"  ⇒ ext

and the demo script as:

function main () {
    mbfl_local_varref(X, 0, -i)
    mbfl_func mbfl_datavar(X)
    echo X=$X >&2
}
function mbfl_func () {
    local -n Y=${1:?}
    Y=1
}

main

Using such preprocessor facilities consumes some computation time; they should be used when we do not care about execution time and when such time is significantly less than running a subshell. Otherwise we should just run the functions in a subshell:

RESULT=$(mbfl_file_extension /path/to/file.ext)

"$RESULT" ⇒ ext

Next: , Previous: , Up: overview   [Contents][Index]

This document describes version 3.0.0-devel.0 of Marcos Bash Functions Library.