26

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.

7 Answers 7

13

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
}
7
  • 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
    Jul 19, 2012 at 13:36
  • Good point. I'll fix it for posterity.
    – chepner
    Jul 19, 2012 at 13:40
  • It looks like attempting to quote the regular expression stops it working as a regular expression in bash >= 3.2.
    – CB Bailey
    Jul 19, 2012 at 14:14
  • 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
    Jul 19, 2012 at 14:51
  • To avoid the eval, just add this first line: var=$1, then use [[ -z "${!var}" ]] && echo Error.
    – chepner
    Jul 19, 2012 at 16:00
11

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.

1
  • 1
    Best Answer if you are using Bash 4.4 or newer!
    – Andy
    Apr 26, 2019 at 14:53
6

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.

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
1
  • Best compatible answer! Over twice as slow as the ${parameter@a} solution, but far more compatible for bash 3.2 cases
    – Andy
    Apr 26, 2019 at 14:54
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=".+"'
1
  • 2
    POSIX 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! Oct 16, 2014 at 22:37
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.

2
  • 1
    This 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
    Jul 19, 2012 at 12:15
  • @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
    Jul 19, 2012 at 12:26
1

The simplest method I currently can think of:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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