LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Bash: Checking existence of environment variable using a string (https://www.linuxquestions.org/questions/programming-9/bash-checking-existence-of-environment-variable-using-a-string-4175467321/)

dwhitney67 06-25-2013 09:13 AM

Bash: Checking existence of environment variable using a string
 
I have a pretty good idea how to check for the existence of an environment variable, however I am having issues referencing the variable's value.

My script is something like:
Code:

#!/usr/bin/env bash

env_vars=( $(cat file.env) )

for var in $(env_vars[@]); do

        env | grep $var

        if [ $? -eq 0 ]; then
            echo $var found
        else
            echo $var not found
        fi
done

file.env contains single-line entries representing the names of the environment variable(s) I want to check. For example:
Code:

LD_LIBRARY_PATH
CLASSPATH

The questions regarding the script above are:

1. How can I be assured that "env | grep $var" returns an exact match, and not another similarly named environment variable (e.g. CLASSPATH vs. MYCLASSPATH vs. CLASSPATH_FOO)?

2. How can I display the current setting of the environment variable if it is found to exist?

Lastly, if it is not too much trouble to ask, how would I develop similar code but for Windows (ie. batch script)?

NevemTeve 06-25-2013 10:04 AM

untested:
Code:

#!/bin/bash

env_vars=( $(cat file.env) )

for var in ${env_vars[@]}; do
    # echo "$var"
    # echo "${!var}"
    if [ "${!var-x}" = "x" -a "${!var-y}" = "y" ]; then
        printf '$%s not found\n' "$var"
    else
        printf 'found: $%s=%s\n' "$var" "${!var}"
    fi
done


grail 06-25-2013 10:15 AM

Unless you wish to use the env_vars array later, I see no point in creating it first, just read the file. Generally I would recommend against reading with for, however, if you are
certain the file contains complete single strings on each line, I would probably go with:
Code:

#!/bin/bash

for var in $(< file.env)
do

    if [[ -n ${!var} ]]
    then
        echo "found $var = ${!var}"
    else
        echo "$var not found"
    fi
done


dwhitney67 06-25-2013 10:32 AM

NevemTeve & Grail -- Thanks for your replies. I was unaware of the ${!var} feature of bash.

My next step is to see if I can translate your suggestions into batch script. :eek:

mina86 06-25-2013 04:35 PM

Note that -n will check whether variable is set to a non-empty value, which is not the same as if it is set. If you are interested in whether variable is set (with possibly empty value) or not, you should use:
Code:

if [ x${!var+set} = xset ]; then
    echo "found: $var=${!var}"
else
    echo "not fonud: $var"
fi

POSIX way would be to use eval (but you might want to sanitise $var first):
Code:

eval "[ x\${$var+set} = xset ]"

dwhitney67 06-25-2013 05:26 PM

mina86 -

I ended up using the -z option (since it seems to suit my needs), along with the critical hints from NevemTeve and Grail. My script, which is larger than the example below, merely is being used to indicate to the end-user whether an environment variable is set or not. If it is set, then I also display its value. Here's an example of what my script looks like:
Code:

#!/usr/bin/env bash

for var in $(< file.env); do
    if [ -z ${!var} ]; then
        echo variable $var is not set
    else
        echo variable $var is set to ${!var}
    fi
done


mina86 06-25-2013 05:52 PM

Oh, that's actually invalid. You need to quote the variables:
Code:

for var in $(< file.env); do
    if [ -z "${!var}" ]; then
        echo "variable $var is not set or empty"
    else
        echo "variable $var is set to ${!var}"
    fi
done


dwhitney67 06-25-2013 05:58 PM

Quote:

Originally Posted by mina86 (Post 4978627)
Oh, that's actually invalid. You need to quote the variables:

Can you please explain the rationale for this? End-users will not be inserting "garbage" into the files, nor have I ever seen an environment variable with a white-space in it.

NevemTeve 06-26-2013 04:02 AM

Script-writers (every programmer, actually) should make a habit of being paranoid with data coming from external sources (keyboard, network, files, environment variables etc)...

mina86 06-26-2013 07:52 AM

Quote:

Originally Posted by dwhitney67 (Post 4978632)
Can you please explain the rationale for this? End-users will not be inserting "garbage" into the files, nor have I ever seen an environment variable with a white-space in it.

You'd be surprised by what end users put into configuration files… Also, maybe in your case only a good citizens will use your script, but it's good to get into habit of assuming that all data coming into your programs may be created by malicious user, especially since it may simply be an user error or something you haven't anticipated but is a legitimate use. The fact that you have never seen an environment variable with a white-space means nothing (and white-space is the least of your concerns in this case).

Some ways to fool or otherwise abuse your script are:
Code:

$ cat a.sh
var=$1
if [ -z ${!var} ]; then
    echo variable $var is not set
else
    echo variable $var is set to ${!var}
fi
$ foo='!= bar' sh a.sh foo
variable foo is not set
$ time foo='/*/*/*/*' sh a.sh foo
^C

real        0m10.878s
user        0m0.060s
sys        0m0.124s
# yeah, 10 seconds was all the patience I had for that command, so I interrupted it
$ time sh a.sh '/*/*/*/*/*'                                                          ZRH:14:51 MTV:05:51 PIT:08:51
^C

real        0m6.896s
user        0m0.064s
sys        0m0.072s
# I had even less patience for this one

And I wasn't even trying things like foo='`rm -rf "$HOME"`', which I'm not 100% sure would work, but if you don't sanitise your input data, the danger is there.

dwhitney67 06-26-2013 08:15 AM

Quote:

Originally Posted by mina86 (Post 4978965)
You'd be surprised...

Thank you for the insight into how to exploit my script. I will make corrections to it immediately.

David the H. 06-28-2013 06:11 AM

As shown, if the variable is set in the environment, then you can just test for its existence directly. Or there's also the built-in declare option you can use instead of env. This might be easiest, if the exact format of the output isn't particularly important.

And if you're using bash v4+, I suggest reading the file directly into an array fist with mapfile.

Code:

#!/usr/bin/env bash

mapfile -t env_vars <file.env

for var in "${env_vars[@]}"; do

    declare -p "$var"

done

If you don't have mapfile, then be sure to use a while+read loop, not a for loop.


Incidentally, bash from v4.2 also has a new -v test. You can check whether the variable name exists directly, without having to use an indirect reference.

Code:

if [[ -v $var ]]; then
    echo "variable $var is set to ${!var}"
    ...


dwhitney67 06-28-2013 06:37 AM

Thanks for the tips. One of the systems that my script runs on, has Bash 3.00.16. The system is a Solaris 10 Sparc.

NevemTeve 06-28-2013 11:38 AM

Reading a whole file into memory is a very good way to eat up every bit of RAM.

David the H. 06-28-2013 12:27 PM

But that depends on how big the file is, of course, and the amount of RAM you have available. I don't think that you're going to get lists of environment variables in the multi-gigabyte range. ;)

Do remember to unset the array when you're done if you're worried about the data sitting there in memory, though.

Anyway, the traditional way to load an array from a file, or to process it directly, for that matter, is like this:
Code:

while IFS='' read -r line || [[ -n $line ]]; do
    array+=( "$line" )
done <infile.txt



All times are GMT -5. The time now is 04:50 AM.