NPS
NPS

Reputation: 6355

Sourceable script that doesn't "pollute" the environment?

Consider these 2 scripts:

envSetter.sh:

#!/bin/bash

export ENV_VAR=6
return 3

sourceable.sh:

#!/bin/bash

function main()
{
    local var=5
    source envSetter.sh
}

main "$@"

unset -f main

envSetter.sh is a script that sets some variables (and possibly performs other actions that affect the current shell (environment)). I need to create a sourceable script that will source envSetter.sh but will also clean up after itself. I.e. after running source sourceable.sh I want my shell to have ENV_VAR set but I don't want to have main (or anything else that sourceable.sh uses) defined.

The above scripts achieve that. However, on top of all that I want sourceable.sh to be able to return whatever exit code main returns. Right now the script returns the result of unset -f main. If I remove that command my script will leave main function defined which I don't want. If I try to use a temporary variable:

main "$@"
result=$?

unset -f main

return $result

It will return what I expect but will also leave result defined which I don't want. I could also not use a main function at all and put all the code in the top level of the script but that would expose even more garbage from inside main (e.g. local var) which I also don't want.

Is there a way to source envSetter.sh (i.e. let it set variables in the current shell), return whatever main returns and not "pollute" my current shell with anything sourceable.sh uses? To clarify: I want envSetter.sh to affect my current shell but I don't want sourceable.sh to.

Upvotes: 3

Views: 230

Answers (3)

KamilCuk
KamilCuk

Reputation: 141225

You can source the script in a subshell and output relevant stuff you want in a source-able form to stdout. Like so:

tmp=$(
   var=5
   # silence stdout so that it does not affect what we want
   source envSetter.sh >&2
   # modify the environment if you want to
   unexport ENV_VAR
   env_var="$ENV_VAR" # for example rename
   # output the environment in a format you want
   declare -p env_var ENV_VAR
   # declare -f function1 function2 # for functions
   # or printf "%q" "$ENV_VAR"
   # or write it to a file printf "%s" "$ENV_VAR" > somefile
   # ie. save the state where you want to
)
ret=$?
# Load hand picked variables.
eval "$tmp"
echo "ENV_VAR=$ENV_VAR ret=$ret"

Upvotes: 0

Gordon Davisson
Gordon Davisson

Reputation: 125858

How about hiding the unset command inside of the main function? Like this:

#!/bin/bash

main() {
    unset -f main
    local var=5
    source envSetter.sh
}

main "$@"

Note that the return status will just propagate through, since source envSetter.sh is the last command in main, it'll become the return status of main, and similarly since main is the last command in the script, it'll become the return status of the script.

BTW, using a shebang on something that's meant to be run with source doesn't make much sense. I tend to use something like this:

#!/bin/echo Run this script with the source or . command.

Upvotes: 6

William Pursell
William Pursell

Reputation: 212298

Do your cleanup in a RETURN trap:

#!/bin/bash

trap 'unset -f main' RETURN

main()
{
    local var=5
    source envSetter.sh
}

main "$@"

Upvotes: 0

Related Questions