What is the simplest scriptable way to check whether a shell variable is exported?

20

4

For some shell sessions I want to be able to print a warning flag if a shell variable is not set and exported.

It is fairly simple to do something like this to print "Error" in the prompt if SET_ME is unset or null.

test_var () { test -z "$1" && echo Error; }
PS1='$(test_var "$SET_ME") \$ '

However this fails to flag if I set SET_ME without exporting it, which is an error that I want to be able to detect. Short of something like $(bash -c 'test -z "$SET_ME" && echo Error;') or grepping the output of export, is there a simple check that I can do to test whether SET_ME has been exported?

A non-POSIX, bash-only solution is completely acceptable.

CB Bailey

Posted 2012-07-19T09:52:14.990

Reputation: 331

Answers

11

Use the declare command and the regular expression matching operator:

test_var () {
    # $1 - name of a shell variable
    var=$1
    [[ -z "${!var}" ]] && echo Error
    [[ $(declare -p $1)  =~ ^declare\ -[aAilrtu]*x[aAilrtu]*\  ]] || echo Error
}

chepner

Posted 2012-07-19T09:52:14.990

Reputation: 5 645

I think that this is what I'm looking for. In theory, the re might need to be more flexible, e.g. if I had a read-only exported variable, but in practice I never use other typeset attributes. – CB Bailey – 2012-07-19T13:36:09.483

Good point. I'll fix it for posterity. – chepner – 2012-07-19T13:40:32.730

It looks like attempting to quote the regular expression stops it working as a regular expression in bash >= 3.2. – CB Bailey – 2012-07-19T14:14:50.023

Also there's an inconsistency, -z "$1" assumes I'm passing the value of a variable to test_var (as I was) whereas declare -p expects its name. I came up with this test which takes the name of a shell variable: test_exported_notnull () { re='^declare -\w*x'; [[ -n $(eval echo \$$1) ]] && [[ $(declare -p "$1") =~ $re ]]; } . – CB Bailey – 2012-07-19T14:51:38.527

To avoid the eval, just add this first line: var=$1, then use [[ -z "${!var}" ]] && echo Error. – chepner – 2012-07-19T16:00:33.060

4

In Bash 4.4 or later, you can use the ${parameter@a} shell parameter expansion to get a list of attributes about a parameter, including if it is exported.

Here is a simple function demonstrating ${parameter@a}, that will tell you if a given variable is exported, given its name:

function is_exported {
    local name="$1"
    if [[ "${!name@a}" == *x* ]]; then
        echo "Yes - '$name' is exported."
    else
        echo "No - '$name' is not exported."
    fi
}

Example of use:

$ is_exported PATH
Yes - 'PATH' is exported.
$ foo=1 is_exported foo
Yes - 'abc' is exported.
$ bar=1; is_exported bar
No - 'abc' is not exported.
$ export baz=1; is_exported baz
Yes - 'baz' is exported.
$ export -n baz; is_exported baz
No - 'baz' is not exported.
$ declare -x qux=3; is_exported qux
Yes - 'qux' is exported.

How it works:

The format returned by ${parameter@a} is one character per attribute, with the meaning of each attribute character coming from the corresponding options from the declare command - in this case, we want to look for x - exported.

Robert Hencke

Posted 2012-07-19T09:52:14.990

Reputation: 41

Best Answer if you are using Bash 4.4 or newer! – Andy – 2019-04-26T14:53:26.883

4

I'm aware the question is 3 years old, however one may find following solution simpler:

[ "$(bash -c 'echo ${variable}')" ]

answers, if the variable is exported and has non-empty value.

ArturFH

Posted 2012-07-19T09:52:14.990

Reputation: 141

3

You can use compgen with its -X option to determine if a variable is exported:

compgen -e -X "!$MAY_BE_EXPORTED_VARIABLE"

E.g.:

$ NOT_EXPORTED="xxx"
$ compgen -e -X '!SHELL'
SHELL
$ compgen -e -X '!NOT_EXPORTED'
$ echo $?
1

Eric Pruitt

Posted 2012-07-19T09:52:14.990

Reputation: 173

Best compatible answer! Over twice as slow as the ${parameter@a} solution, but far more compatible for bash 3.2 cases – Andy – 2019-04-26T14:54:58.263

2

If I resign myself to having to use export and grep, the simplest test is probably something like this.

export | grep -Eq '^declare -x SET_ME='

or if I want non-null as well:

export | grep -Eq '^declare -x SET_ME=".+"'

CB Bailey

Posted 2012-07-19T09:52:14.990

Reputation: 331

1POSIX 7 says that export is unspecified, and defines a precise format for export -p similar to bash export but different. But bash seems to ignore POSIX and use the same format as export for export -p! – Ciro Santilli 新疆改造中心法轮功六四事件 – 2014-10-16T22:37:06.510

1

The export command, given without parameters, gives a list of exported names in the current environment:

$ FOO1=test
$ FOO2=test
$ export | grep FOO
$ export FOO2
$ export | grep FOO
declare -x FOO2="test"

Some cutting and sed'ing gets rid of the fluff:

export | cut -d' ' -f 3- | sed s/=.*//

There's your list of exports, ready for further processing.

DevSolar

Posted 2012-07-19T09:52:14.990

Reputation: 3 860

1This does work but I was hoping for a lighter answer with fewer implied forks (hence "Short of [...] grepping the output of export") as my planned use is in my prompt. – CB Bailey – 2012-07-19T12:15:17.450

@CharlesBailey: I see. I came to this by searching the bash manpage for export, and this was the only thing I came up with. No help from the shell escapes either. The export is builtin anyway, but I doubt you can avoid the grep. – DevSolar – 2012-07-19T12:26:04.673

1

The simplest method I currently can think of:

[ bash -c ': ${v1?}' 2>/dev/null ]

Dani

Posted 2012-07-19T09:52:14.990

Reputation: 11