You are not logged in.

#1 2016-01-09 10:28:39

xaos52
The Good Doctor
From: Planet of the @pes
Registered: 2015-09-30
Posts: 695

Bash scripting: local and unset

Using local and unset

script #1

#!/usr/bin/env bash

f() {
    trap 'declare -p HOME' DEBUG    # runs the declare command after every 'simple command'
    printf '%s\n' 'in function f'
    local HOME
    HOME=
    HOME=egrzfri
    unset -v HOME
    printf '%s\n' 'return from f'
}

trap 'declare -p HOME' DEBUG    # runs the declare command after every 'simple command'
printf '%s\n' 'start script'
printf '%s\n' 'call function f'
f
printf '%s\n' 'back in main script'

Save the script somewhere in your PATH, name it as you wish and run the script with:

script-name='your-chosen-script-name'
bash -x ./${script-name} |& tee ./${script-name}.log

Check the log file for the value of HOME. Note that
- At script level HOME is inherited from environment
- Inside a function:
. as long as HOME has not been declared local, it keeps its global value
. setting the variable local in the function does not initialize it.
  Bash just marks the name as belonging to the local scope, no value
  is associated with it yet.
  It is considered good practice to initialize the variable explicitly
  though  that is not a requirement.
. assigning HOME a value, it keeps that local value until either it is
  assigned a new value or control leaves the function scope
. unset the variable within the function scope does not affect the
  global value
. leaving the function scope restores the global variable

These observations hold for bash variables (parameters) as well.

Q&A

Q. Upon entering the function f, HOME has the global value inherited from the
environment. After specifying HOME as a local variable, is it possible for HOME
to regain its inherited global value within function f?

A. Yes, but it is not obvious:

Unsetting HOME within f sets the value of HOME to the empty string.
However, it is possible to restore the global value by creating a new function
on the function stack from within f: function g.

script #2

#!/usr/bin/env bash

f() {
    trap 'declare -p HOME' DEBUG
    printf '%s\n' 'in function f'
    local HOME
    HOME=
    HOME=egrzfri
    unset -v HOME
    g(){
        trap 'declare -p HOME' DEBUG
        unset -v HOME
        printf '%s\n' 'in function g'
    }
    printf '%s\n' 'call function g'
    g
    printf '%s\n' 'back in function f'
    printf '%s\n' 'return from f'
}

trap 'declare -p HOME' DEBUG
printf '%s\n' 'start script'
printf '%s\n' 'call function f'
f
printf '%s\n' 'back in main script'

After 'unset -v HOME' in function g, HOME regains the value inherited
from the environment, just as it had never been changed in function f.

Upon returning from g, HOME has again the value inherited from the environment.

Executive Summary

johnraff wrote:

Unset a variable in an inner function and it loses it's local nature in the wrapping function!


Q. So, if you are writing a new script, should you unset your variables
before using them?

A. In general, no.

You can use environment variables as they are, unless you want to override them
explicitly. Just assigning a new value is sufficient, no need to unset it first.

If your variables are bash parameters, just assign values. no need for unset.
Do take care *not* to inadvertently override environment variables.
That is why it is best to use all capitals for environment variables
another schema for bash parameters (all lowercase or camelcase, as you prefer)

The case where you source another script into your script needs special attention.
You may be importing global parameters and functions.

When you are importing global variables, but you don't use those variables in you script,
and you don't use functions that use those variables, then you can clean up the variables
namespace by unsetting those variables explicitly.

When the imported functions use variables that are *not* declared local, then you will
import such variables when you call such functions from your script, thus polluting
your variable namespace: variables take up entries in the namespace and their values
take up memory space.

If you have access to the sourced script, change it to make the variables local.

If you don't have acces to the sourced script, inform the author of the script and
unset those variables in your own script until the sourced script has been changed.

Want to know more about local variables head over to bash-hackers
There is a script in there that may rack your brains, but also shows how you can
explore the function stack of a bash script, and do some specific debugging in bash.

Q. So, if you are writing a new script, should you initialize your variables
before using them?

A. Yes.

Initialize them to the type they will hold. This is extra information for someone else reading
the script and trying to figure out what it does.

Recommendation

When you use unset, use the option -v for variable or -f for function.
Without option bash will first search for a variable with the specified name and if it does not
find any it will go searching for a function of that name.


References:
  1. http://wiki.bash-hackers.org/commands/builtin/unset

  2. http://wiki.bash-hackers.org/scripting/ … able_names

  3. http://wiki.bash-hackers.org/scripting/ … ialization


Thats all for now, folks smile

Have fun!

Offline

Board footer

Powered by FluxBB