4

I would like to include files into a shell script as "support" scripts. Since they set things up and check the environment, I only want to let them be executed once.

My approach was to create a global array, check if the script name exists in that array. f it doesn't, add it to the array and "source" the external file.

The current code looks like this:

 function array_contains() { # array, value
    local element
    for element in $1; do
        [[ "$element" == "$2" ]] && return 0
    done

    return 1
}

declare -a LOADED_SUPPORT_SCRIPTS

function require_support() { # file
    # Check if we loaded the support already
    array_contains ${LOADED_SUPPORT_SCRIPTS[@]} $1 && return 0

    # Add loaded support to array
    LOADED_SUPPORT_SCRIPTS=("${LOADED_SUPPORT_SCRIPTS[@]}" "$1")

    log -i "Including support for '$1'"
    source "$BASE_DIR/supports/$1.sh"

    return 1
}

While this should work in theory (at least for me), this code just fails every single time. Somehow the array gets reset (even though I never access it somewhere else) and always contains a "/" entry in the beginning.

From further googling my problem I found this "solution" which needs a new variable name inside every script that I want to include - like C/C++ does. While this is indeed a good idea, i would like to keep my code as small as possible, I do not really care about the performance of array iterations.

I really would like to know if there is an alternative way, or what I did wrong in my code that could be fixed. Thanks in advance!

spaceemotion
  • 1,404
  • 4
  • 24
  • 32
  • Side point: [bash added associative arrays a while ago](http://www.gnu.org/software/bash/manual/html_node/Arrays.html). – jthill May 31 '14 at 12:37

4 Answers4

3

Perhaps you can try using Shell Script Loader for that. See a post about it in a famous similar thread: https://stackoverflow.com/a/3692080/445221.

Community
  • 1
  • 1
konsolebox
  • 72,135
  • 12
  • 99
  • 105
3

You could just check whether the function you expect to be created by the support script exists. For example, if you name the support scripts after (at least one of) the methods they contain:

for path in supports/*.sh
do
    file="$(basename "$path")"
    function="${file%.sh}"
    if ! declare -F "$function"
    then
        source "$path"
    fi
done
l0b0
  • 55,365
  • 30
  • 138
  • 223
2

You don't need a control on the "sourcing side", you can have the script control for that itself.

Simply add something like the following to the beginning of the script you only want to be sourced once (replace "MY_LIB" with some unique name):

[ -n "${MY_LIB}" ] && return; MY_LIB=0; # pragma once

If you later want to enable the option to source it again, you can use:

unset MY_LIB

If you want to dynamically check if the script is available in your client scripts, use the same basic logic:

if [ -n "${MY_LIB}" ] then
    # use the lib
else
    # use some alternate solution...
fi

Alternatively, for one liners:

[ -n "${MY_LIB}" ] && echo "do something here using the lib";
BuvinJ
  • 10,221
  • 5
  • 83
  • 96
0

@BuvinJ I like your solution but the only way I got it working was by adding export like:

[ -n "${MY_LIB}" ] && return; export readonly MY_LIB=0;
Rom's
  • 62
  • 3
  • Interesting. I guess you either "loaded" the library in a different manner than I (like using source vs executing it?). Or else this is related to using different shell script interpreters? – BuvinJ Jul 25 '22 at 17:25