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!

1

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

Comments

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

Comments

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";

2 Comments

Thanks! I use test -v MY_LIB && return; readonly MY_LIB=0; # pragma once to avoid MY_LIB: unbound variable errors, because —in every script— I use the life saver😊 set -o nounset.
You're welcome! Nice tweak for that context!
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;

2 Comments

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?
Shellcheck warns that "This applies export to the variable named readonly, which is probably not what you want. [...]". shellcheck.net/wiki/SC2316 can be seen.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.