28

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

14

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
}
8
  • 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
  • 1
    I would also add local var=$1 since otherwise you will have a global variable var hanging around. Feb 14 at 17:03
12

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
0

The simplest method I currently can think of:

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .