Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I just ran into a problem that shows me I'm not clear on the scope of shell variables.

I was trying to use bundle install, which is a Ruby command that uses the value of $GEM_HOME to do its work. I had set $GEM_HOME, but the command ignored that value until I used export, as in export GEM_HOME=/some/path.

I read that this makes the variable somehow "global" (also known as an environment variable), but I don't understand what that means. I know about globals in programming, but not across distinct programs.

Also, given that my setting such variables applies only to the current shell session, how would I set them for, say, a daemonized process?

What scopes can shell variables have?

share|improve this question
add comment

4 Answers

The processes are organized as a tree: every process has a unique parent, apart from init which PID is always 1 and has no parent.

The creation of a new process goes generally through a pair of fork/execv system calls, where the environment of the child process is a copy of the parent process.

To put a variable in the environment from the shell you have to export that variable, so that it is visible recursively to all children. But be aware that if a child change the value of a variable, the changed value is only visible to it and all processes created after that change (being a copy, as previously said).

Take also into account that a child process could change its environment, for example could reset it to default values, as is probably done from login for example.

share|improve this answer
    
Ah! OK, let's see if I understand this. In the shell, if I say FOO=bar, that sets the value for the current shell process. If I then run a program like (bundle install), that creates a child process, which doesn't get access to FOO. But if I had said export FOO=bar, the child process (and its descendants) would have access to it. One of them could, in turn, call export FOO=buzz to change the value for its descendants, or just FOO=buzz to change the value only for itself. Is that about right? –  Nathan Long Dec 24 '11 at 12:17
    
@NathanLong That's not exactly it: in all modern shells, a variable is either exported (and so any change in value is reflected in the environment of descendents) or not exported (meaning that the variable is not in the environment). In particular, if the variable is already in the environment when the shell starts, it is exported. –  Gilles Dec 24 '11 at 16:02
add comment

At least under ksh and bash, variables can have three scopes, not two like all remaining answers are currently telling.

In addition to the exported (i.e. environment) variable and shell unexported variable scopes, there is also a third narrower one for function local variables.

Variables declared in shell functions with the typeset token are only visible inside the functions they are declared in and in (sub) functions called from there.

This ksh / bash code:

# Create a shell script named /tmp/show that displays the scoped variables values.    
echo 'echo [$environment] [$shell] [$local'] > /tmp/show
chmod +x /tmp/show

# Function local variable declaration
function f
{
    typeset local=three
    echo "in function":
    . /tmp/show 
}

# Global variable declaration
export environment=one

# Unexported (i.e. local) variable declaration
shell=two

# Call the function that creates a function local variable and
# display all three variable values from inside the function
f

# Display the three values from outside the function
echo "in shell":
. /tmp/show 

# Display the same values from a subshell
echo "in subshell":
/tmp/show

produces this output:

in function:
[one] [two] [three]
in shell:
[one] [two] []
in subshell:
[one] [] []

As you can see, the global variable is displayed from all three locations, the unexported variables is not displayed outside the current shell and the function local variable has no value outside the function itself.

share|improve this answer
add comment

The best explanation I can find about exporting is this one:

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

The variable set within a subshell or child shell is only visible to the subshell in which it is defined. The exported Variable is actually made to be an environment variable. So to be clear your bundle install executes its own shell which doesn't see the $GEM_HOME unless it is made an environment variable aka exported.

You can take a look at the documentation for variable scope here:

http://www.tldp.org/LDP/abs/html/subshells.html

share|improve this answer
    
Ah, so I was incorrect to use the term "environment variable" for FOO=bar; you have to use export to make it one. Question corrected accordingly. –  Nathan Long Dec 24 '11 at 12:35
    
Take a look at the link I have added in. –  Karlson Dec 26 '11 at 19:37
add comment
up vote 1 down vote accepted

They are scoped by process

The other answerers helped me to understand that shell variable scope is about processes and their descendants.

When you type a command like ls on the command line, you're actually forking a process to run the ls program. The new process has your shell as its parent.

Any process can have its own "local" variables, which are not passed to child processes. It can also set "environment" variables, which are. Using export creates an environment variable. In either case, unrelated processes (peers of the original) will not see the variable; we are only controlling what child processes see.

Suppose you have a bash shell, which we'll call A. You type bash, which creates a child process bash shell, which we'll call B. Anything you called export on in A will still be set in B.

Now, in B, you say FOO=b. One of two things will happen:

  • If B did not receive (from A) an environment variable called FOO, it will create a local variable. Children of B will not get it (unless B calls export).
  • If B did receive (from A) an environment variable callled FOO, it will modify it for itself and its subsequently forked children. Children of B will see the value that B assigned. However, this will not affect A at all.

Here's a quick demo.

FOO=a      # set "local" environment variable
echo $FOO  # 'a'
bash       # forks a child process for the new shell
echo $FOO  # not set
exit       # return to original shell
echo $FOO  # still 'a'

export FOO # make FOO an environment variable
bash       # fork a new "child" shell
echo $FOO  # outputs 'a'
FOO=b      # modifies environment (not local) variable
bash       # fork "grandchild" shell
echo $FOO  # outputs 'b'
exit       # back to child shell
exit       # back to original shell
echo $FOO  # outputs 'a'

All of this explains my original problem: I set GEM_HOME in my shell, but when I called bundle install, that created a child process. Because I hadn't used export, the child process didn't receive the shell's GEM_HOME.

Un-exporting

You can "un-export" a variable - prevent it from being passed to children - by using export -n FOO.

export FOO=a   # Set environment variable
bash           # fork a shell
echo $FOO      # outputs 'a'
export -n FOO  # remove environment var for children
bash           # fork a shell
echo $FOO      # Not set
exit           # back up a level
echo $FOO      # outputs 'a' - still a local variable
share|improve this answer
    
When you say "it will modify it for itself and its children" you should clarify that only children created after the modification will see the modified value. –  enzotib Jan 4 '12 at 21:28
    
@enzotib - good point. Updated. –  Nathan Long Jan 9 '12 at 20:18
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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