I have a bash script with a variable called VAR_A

I also have a local env variable called VAR_A

The bash script calls the command:

echo ${VAR_A}

I am not able to change the variable in the script to another name, but I can change how the script calls the echo command.

How can I modify the echo command to ensure it's printing the local env variable instead of the one provided in the script?


The situation:

User has an existing .bashrc file which on login, sets:

export VAR_A

This allows the user to:

~]$ echo ${VAR_A}

I have a configuration file for some bash scripts


I have a bash script:


. ../configuration  # imports config file with some values

#  do stuff

echo ${VAR_A}

Executing the script from the terminal (bash shell as logged in user) prints:

~]$ ./run_script.sh

I need it to print:

~]$ ./run_script.sh
  • 1
    Your question isn't clear. Do you mean to say that you can modify the script but cannot change the variable name?
    – devnull
    Mar 24 '14 at 6:37
  • @devnull yes, I can change in the script how echo is invoked (as-to print the local env variable, ie. as if the user typed echo ${VAR_A} from their bash prompt), but I cannot change either the user's existing env variable nor the name of the bash script's variable.
    – SnakeDoc
    Mar 24 '14 at 7:06
  • And the variable in question is an environment variable set outside the script?
    – devnull
    Mar 24 '14 at 7:08
  • @devnull correct. (sorry for the late response, was pretty late here last night)
    – SnakeDoc
    Mar 24 '14 at 14:36
  • @devnull it is setup by .bashrc when the user logs in.
    – SnakeDoc
    Mar 24 '14 at 14:42

Shell variables are initialised from environment variables in every shell, you can't get around that.

When the shell starts, for every environment variable it receives that has a valid name as a shell variable, the shell assigns the corresponding shell variable the corresponding value. For instance, if your script is started as:

env VAR_A=xxx your-script

(and has a #!/bin/bash - she-bang), env will execute /bin/bash and pass VAR_A=xxx to that bash command, and bash will assign its $VAR_A variable the value xxx.

In the Bourne shell and in the C-shell, if you assign a new value to that shell variable, it doesn't affect the corresponding env variable passed to later commands executed by that shell, you have to use export or setenv for that (note however that in the Bourne shell if you unset a variable, it removes both the shell variable and environment variable).


env VAR=xxx sh -c 'VAR=yyy; other-command'

(with sh being the Bourne shell, not modern POSIX shells) Or:

env VAR=xxx csh -c 'set VAR = yyy; other-command'

other-command receives VAR=xxx in its environment, not VAR=yyy, you'd need to write it:

env VAR=xxx sh -c 'VAR=yyy; export VAR; other-command'


env VAR=yyy csh -c 'setenv VAR yyy; other-command'

For other-command to receive VAR=yyy in its environment.

However, ksh (and POSIX as a result, and then bash and all other modern Bourne-like shells as a result) broke that.

Upon start-up those modern shells bind their shell variable to the corresponding environment variable.

What that means is that a script may clobber the environment just by setting one of its variables even if it doesn't export it. Some shells are even known to remove the environment variables it cannot map to shell variables (which is why it's recommended to only use shell-mappable variable names for environment variable names).

That's a main reason why by convention, all uppercase variables should be reserved for environment variables.

To work around that, if you want the commands executed by your script to receive the same environment as the shell interpreting your script received, you'd need to store that environment somehow. You can do it by adding:

my_saved_env=$(export -p)

at the start of your script, and then run your commands with:

(eval "$my_saved_env"; exec my-other-command and its args)

In the bosh and mksh shells, local variables in functions don't clobber environment variables (as long as you don't export them), though note that shell builtins which use some special variables (like HOME for cd, PATH for executable lookup...) would use the value of the shell (local) variable, not the environment one.

$ env VAR=env mksh -c 'f() { local VAR=local; echo "$VAR"; printenv VAR; }; f'
  • This may work. (especially your last part to save the env variable before the script clobbers it). The clobbering value is coming from an import into the bash script using the . <path to file> method at the very top of the script beneath the #!/bin/bash header. Maybe I don't understand importing fully, am I able to import the other script (and thus the offending variable) after using your trick to save the existing env variable?
    – SnakeDoc
    Mar 24 '14 at 16:15
  • 1
    The . command tells the shell to read and interpret the code in the given file. It's like #include in C. export -p dumps the values of the currently exported variables (the ones explicitly exported with export and the ones that were in the environment that the shell received on startup). So you'll want to run it at the point where you want the values saved. Mar 25 '14 at 15:03
  • Yet another trick for Bash would be wrapping the entire script-body in a function, and do some magic to unexport and mark the environment as local: ::() { source <(export -p | sed 's/-/+/'); script-body; }; :: "$@"
    – alecov
    Sep 2 '20 at 6:17

You could make the variable VAR_A read only by adding a line:

readonly VAR_A

at the top of your script. This would cause the value of VAR_A to be preserved as per your local environment.

readonly: readonly [-aAf] [name[=value] ...] or readonly -p

Mark shell variables as unchangeable.

Mark each NAME as read-only; the values of these NAMEs may not be
changed by subsequent assignment.  If VALUE is supplied, assign VALUE
before marking as read-only.

  -a        refer to indexed array variables
  -A        refer to associative array variables
  -f        refer to shell functions
  -p        display a list of all readonly variables and functions

An argument of `--' disables further option processing.

Exit Status:
Returns success unless an invalid option is given or NAME is invalid.

The following example should make it clear:

$ export FOO="somevalue"        # environment variable FOO set to somevalue
$ cat test                      # test script
echo $FOO                       # print the value of FOO
readonly FOO                       # set FOO to local
FOO="something"                 # attempt to modify FOO
echo $FOO                       # print the value of FOO -- you would see the value that was inherited from the environment
$ bash test
test: line 3: FOO: readonly variable

If you want to print local variable VAR_A, you must call in its local scope, otherwise, it will print value of global variable VAR_A:

#! /bin/bash 

VAR_A="I'm global"

function lexical() {
    local VAR_A="I'm lexical"
    echo $VAR_A

echo -n "Global VAR_A: "
echo $VAR_A
echo -n "Lexical VAR_A: "

Run it:

$ ./test.sh 
Global VAR_A: I'm global
Lexical VAR_A: I'm lexical

You can read more about local in bash from man page:

local [option] [name[=value] ...]
              For each argument, a local variable named name is  created,  and
              assigned  value.   The option can be any of the options accepted
              by declare.  When local is used within a function, it causes the
              variable  name  to have a visible scope restricted to that func‐
              tion and its children.  With no operands, local writes a list of
              local  variables  to the standard output.  It is an error to use
              local when not within a function.  The return status is 0 unless
              local  is  used outside a function, an invalid name is supplied,
              or name is a readonly variable.
  • not sure if this works. I'm trying to print the contents of the user's existing ${VAR_A} as-if the user literally typed echo ${VAR_A} from their terminal, not using the script. But since the user's existing ${VAR_A} has a namespace collision with the script, I'm unsure which one is printing when called in the script?
    – SnakeDoc
    Mar 24 '14 at 7:08


grep -zoP '^VAR_A=\K(?s).*' "/proc/$PPID/environ"

It is not entirely clear what you are asking as a "local env variable" is a contradiction. Environment variables are global, not local.

You have three types of variables:

  • exported global variables (environment variables).
  • global variables.
  • local variables.

Global variables are scoped to the shell process. Any shell function running in that process can read and modify global variables. Exported variables are put into the processes environment and available to child processes. Local variables are local to a shell function.

What I think you're asking is whether you can access an environment variable that exists prior to invoking a script after that script modifies the variable.

In pure bash, you cannot do this. When the script runs, all the environment variables are exported global variables. When the script modifies VAR_A, it is changed in the environment.

If you are using Linux, you can access the parent's environment through the proc filesystem. The parent process will usually have the original environment variable you are looking for:

grep -zoP '^VAR_A=\K(?s).*' "/proc/$PPID/environ"

That will extract the environment variable VAR_A from the parent process's ($PPID) environment and print out just the value part. It relies on you being able to read your parent's environment (same UID) and that the parent has the variable you are looking for.

  • i think you are getting at my core problem. the existing env variable is set by .bashrc when the user logs in... so it already exists and the user can type at the terminal echo ${VAR_A} and it prints the result. Running the script, the script sets ${VAR_A} to something, and echo's that. But when the script terminates, the user can type echo ${VAR_A}` again and it will correctly print the value set by .bashrc since that bash session (for the script) has terminated. I would want the script to print the value set by .bashrc
    – SnakeDoc
    Mar 24 '14 at 14:41
  • @SnakeDoc the details of this will depend on your script. Without showing us how the script works and how exactly you're setting the variable in .bashrc, you're asking people to shoot blind.
    – terdon
    Mar 24 '14 at 15:52
  • @terdon I was afraid of that... it's spread over 2 bash script files and the user's .bashrc... but basically, .bashrc uses a normal VAR_A="someValue" export VAR_A in it... the script imports another script which has the definition VAR_A="someOtherValue". This effectively overrides the .bashrc VAR_A's value. In the script, when I use echo ${VAR_A} is prints someOtherValue, instead of someValue. I need it to print someValue. Hope that's clear.
    – SnakeDoc
    Mar 24 '14 at 16:10
  • @SnakeDoc please edit your question and include this information, comments are hard to read and easy to miss. You need to explain exactly where you are running the echo (in a login shell? non-login shell? in a script?) and give as many details as you can.
    – terdon
    Mar 24 '14 at 16:14
  • @terdon I just updated with some sample scripts in my OP. hope it helps make things more clear.
    – SnakeDoc
    Mar 24 '14 at 16:22

What is happening is that the config file VAR_A is overwriting the contents of the VAR_A coming from the .bashrc.

Hence the solution suggests itself: Before you source the config file, save the contents of the VAR_A, as shown below.

VAR_A_sav=${VAR_A} # assuming that VAR_A_sav does NOT exist in configuration

. ../configuration # that is, you must choose a name not existing in config.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.