How to find out environment variables set after logging into a shell session

1

2

How can I find out the environment variables set after logging into a shell session?

My two dummy solutions have been so far as follows:

_VariableName1="VarValue1";export _VariableName1;
_VariableName2="VarValue2";export _VariableName2;

...

set | grep '_' 

… which almost shows the variables set after login (those different than the vars set .bash_profile and .bashrc). This approach makes me constantly forget about the underscore in the vars – and the old gurus at work laugh at me when they see it ;)

The second approach is to use "human readable variables", but to run after login or sudo:

 su - username the 
 set > /tmp/vars.before 

set the vars

 VariableName1="VarValue1";export VariableName1;
 VariableName2="VarValue2";export VariableName2;

set > /tmp/vars.after

… and then run the following command:

 comm -13 /tmp/vars.before /tmp/vars.after

or

 comm --nocheck-order -3 /tmp/vars.before /tmp/vars.after

depending on the comm binary etc.

So what is the cleanest or most clever way of finding out?

Yordan Georgiev

Posted 2012-10-24T06:37:02.790

Reputation: 133

Answers

3

One approach is to override export and unset with functions that keep track of your variables.

Please notice that for this to work you must export variables like this: export <variable>=<value>.

The two functions must be called export and unset and maintain a list of your variables in a file, which I'll name ~/.track$$ ($$ is the PID of your current shell):

export ()
{
    if echo $@ | egrep -q '[^=]+=[^=]+' && builtin export "$@" && echo $- | grep -q i; then          # [1]
        touch ~/.track$$                                                                             # [2]
        cp -fa ~/{.track$$,.track$$.bak} 2> /dev/null;                                               # [3]  
        grep -v "^$(echo $@ | sed 's/\([^=]=\).\+/\1/')" < ~/.track$$.bak > ~/.track$$ 2> /dev/null; # [4]
        echo $@ >> ~/.track$$;                                                                       # [5]
    fi
}

unset ()
{
    if builtin unset $@ && echo $- | grep -q i; then                # [1]
        touch ~/.track$$                                            # [2]
        cp -fa ~/{.track$$,.track$$.bak} 2> /dev/null;              # [3]
        egrep -v "^$@=" < ~/.track$$.bak > ~/.track$$ 2> /dev/null; # [4]
    fi
}

Additionally you need to find out which environment variables you have set. An alias like this will make it:

alias iset="cat ~/.track$$ 2>/dev/null"

An alias like this:

alias ireset="rm ~/.track$$ >/dev/null 2>&1"

can be used to reset the list.

To keep the number of ~/.track$$ files to a minimum we can use this housekeeping command:

rm $(ls -d ~/.* | egrep 'track[0-9]+$|track[0-9]+\.bak$' | egrep -v $(ps -ef | grep bash | grep -v grep | awk '{print $2}' | tr '\n' '|' | sed 's/|$//')) > /dev/null 2>&1

Where should we to put these functions, aliases, etc?

I'd would recommend that you add the functions, aliases and the housekeeping command to the end of ~/.bashrc, so that subshells get the functions too. Depending on your distro and how /etc/profile, /etc/bash.bashrc and friends are executed you may define them too early (in some distros ~/.profile sources ~/.bashrc or the other way round) so you may have to fine tune it.

Then log out and log in again.

Now, if you run export <variable>=<value> in your bash session the function export:

  • [1] checks whether the parameter is well formed (this is to avoid spurious entries when typing export MYVAR, because its return value is 0 and would create an entry in ~/.track$$)

  • [1] then it executes builtin export <variable>=<value> (this sets the variable, regardless of what happens afterwards)

  • [1] then it greps $- to see whether this is an interactive shell (see this answer When I ssh into a ubuntu machine, what kind of shell am I using [shameless plug]). By default, subshells don't inherit functions, so scripts won't create ~/.track$$ files. If a script uses #!/bin/bash -i, though, it will. This will prevent it.

  • [2] then it touches ~/.track$$ to make sure it exists and

  • [3] makes a backup copy

  • [4] then it checks whether the variable already exists, if that's the case it is deleted from ~/.track$$

  • [5] finally, it adds a line to ~/.track$$

Similarly, if you type unset <variable> and hit Enter the function unset:

  • [1] executes builtin unset <variable> (this unsets the variable, regardless of what happens afterwards)

  • [1] if unset was successful it checks, as in the export function above. whether this is an interactive shell (see this answer When I ssh into a ubuntu machine, what kind of shell am I using [shameless plug]). By default, subshells don't inherit functions, so scripts won't create ~/.track$$ files. If a script uses #!/bin/bash -i, though, it will. This will prevent it.

  • [2] then it touches ~/.track$$ to make sure it exists and

  • [3] then makes a backup copy of ~/.track$$

  • [4] removes the variable entry from ~/.track$$

What is the builtin keyword I used in both functions? Since export or unset are shell builtins (that is, commands that come with the shell) I need to use builtin, which is itself a builtin command (from man bash):

builtin shell-builtin [arguments]

Execute the specified shell builtin, passing it arguments, and return its exit status. This is useful when defining a function whose name is the same as a shell builtin, retaining the functionality of the builtin within the function. The cd builtin is commonly redefined this way. The return status is false if shell-builtin is not a shell builtin command.

The housekeeping command lists and filters the active ~/.track$$ files out and deletes the rest.

What you get with this setup:

  • No need to use underscores anymore. Simply use the familiar export command (which has been overriden with a function) and it will work.

  • Each bash session has its own ~/.track$$ file. No collisions among shells.

  • As side effect from the above, subshells don't inherit the parent's ~/.track$$ file, although they inherit all environment variables.

  • Environment variables set in sourced files (. file or source file) are added to ~/.track$$.

  • Environment variables set in a () subshell are also (incorrectly) tracked, because $$ expands to the process ID of the current shell, not the subshell (see man bash).

  • Variables are listed from first to last exported.

  • Variables set by .profile and other scripts that you reassign (even with the same value) will be listed by iset. For instance, HOME is usually set by .profile. If you do export HOME=/usr/local/bin:$HOME and run iset you will see HOME listed.

And now some examples:

  • Exported variables are shown with iset:

    $ export MYVAR1=0987654321; iset
    MYVAR1=0987654321
    
  • Redefined variables are dealt with properly:

    $ export MYVAR2="this is a string"; iset
    MYVAR1=0987654321
    MYVAR2=this is a string
    $ export MYVAR2="this is a different string for the same variable"; iset
    MYVAR1=0987654321
    MYVAR2=this is a different string for the same variable
    
  • Environment according to env matches iset output:

    $ env|grep MYVAR
    MYVAR2=this is a different string for the same variable
    MYVAR1=0987654321
    
  • Readonly variables are not added to ~/.track$$:

    $ export EUID=0; iset
    -bash: EUID: readonly variable
    MYVAR1=0987654321
    
  • Old, no longer used track files are deleted when ~/.bashrc is executed (for instance, when creating a subshell):

    $ ls -1 ~/.track*
    .track11002
    .track11002.bak
    .track21774
    .track21774.bak
    .track2923
    .track2923.bak
    .track7382
    .track7382.bak
    .track8374
    .track8374.bak
    $ echo $$
    2923
    $ bash
    <subshell>$ ls -1 ~/.track*
    .track2923
    .track2923.bak
    

jaume

Posted 2012-10-24T06:37:02.790

Reputation: 4 947

You're welcome, I'm glad I could help. – jaume – 2012-11-06T07:29:15.440

Your approach can be defeated in 3 ways: 1. Unset a var which was exported by .profile. (the change won't be shown) 2. Unset and re-export (to original value) a var which was exported by .profile. (it will be incorrectly listed by iset) 3. export myvar; myvar=value (iset will show the wrong value). – Hugh Allen – 2012-11-06T13:29:05.903

@Hugh Allen: I agree with 3., export var=value must be used, I edited my answer to make it clearer, thanks for perusing it and pointing that out. As of 2., if I understand correctly, the OP wishes to find out environment vars that are set (ie, that are assigned a value) after logging in, so any var set afterwards must be tracked. Consequently, vars exported by .profile, if set later, are added to iset. Regarding 1., I don't see how a var can be deleted from iset's output if it was never added to it. 1. is desired behavior: vars set by .profile and not reassigned aren't monitored. – jaume – 2012-11-07T06:57:46.307

You might be right. I had assumed Yordan wanted to know about any changes including variables which were unset, as per his second approach using a comparison command. – Hugh Allen – 2012-11-07T10:51:17.487

1

If you're interested in environment variables only, you should use env instead of set. I'm not quite sure whether I understand correctly what you're trying to achieve, but it seems that

env | sort > /tmp/vars.before

after logging in, followed by

env | sort > /tmp/vars.after
comm -13 /tmp/vars.before /tmp/vars.after

at some later point does the right thing, at least if none of your environment variables contains a string that includes a newline. (If it does, I'd either use env -0 | sort -z to get the environment variables separated by NULLs and then use perl for comparison, or program the entire thing in perl.)

Uwe

Posted 2012-10-24T06:37:02.790

Reputation: 1 043

0

If you want to know what environment variables have been set, simply enter the following command:

printenv

Bruno

Posted 2012-10-24T06:37:02.790

Reputation: 1