views:

5879

answers:

4

A simple Bash variable test goes:

${varName:?    "${varName} is not defined"}

I'd like to re-use this, by putting it in a function. How please?

Following fails

#
# Test a variable exists
tvar(){
 val=${1:?    "${1}    must be defined, preferably in $basedir"}
 if [ -z ${val}  ]
     then 
     echo Zero length value 
 else
     echo ${1} exists, value ${1}
 fi
}

I.e. I need to exit if the test fails.

A: 

I don't have understood exactly what do you need. I'm trying to replying you despite of this.

I.e. I need to exit if the test fails.

The code:

${varName:?    "${varName} is not defined"}

will return a non zero exit code when there is not a variable named "varName". The exit code of the last command is saved in $?.

About your code:

val=${1:?    "${1}    must be defined, preferably in $basedir"}

Maybe it is not doing what you need. In the case that $1 is not defined the "${1}" will be substituted with nothing. Probably you want use the single quotes that literally writes ${1} without substitution.

val=${1:?    '${1}    must be defined, preferably in $basedir'
Andrea Francia
I want to encapsulate the test in a function, to allow me to call it withdefined SomeVariableNameso that I can re-use it.
DaveP
DaveP
+3  A: 

What you're looking for is indirection.

assertNotEmpty() {
    : "${!1:? "$1 is empty, aborting."}"
}

That causes the script to abort with an error message if you do something like this:

$ foo=""
$ assertNotEmpty foo
bash: !1:  foo is empty, aborting.

If you just want to test whether foo is empty, instead of aborting the script, use this instead of a function:

[[ $foo ]]

For example:

until read -p "What is your name? " name && [[ $name ]]; do
    echo "You didn't enter your name.  Please, try again." >&2
done

Also, note that there is a very important difference between an empty and an unset parameter. You should take care not to confuse these terms! An empty parameter is one that is set but just set to an empty string. An unset parameter is one that doesn't exist at all.

The previous examples all test for empty parameters. If you want to test for unset parameters and consider all set parameters OK, whether they're empty or not, use this:

[[ ! $foo && ${foo-_} ]]

Use it in a function like this:

assertIsSet() {
    [[ ! ${!1} && ${!1-_} ]] && {
        echo "$1 is not set, aborting." >&2
        exit 1
    }
}

Which only aborts the script when the parameter name you pass denotes a parameter that isn't set:

$ ( foo="blah"; assertIsSet foo; echo "Still running." )
Still running.
$ ( foo=""; assertIsSet foo; echo "Still running." )
Still running.
$ ( unset foo; assertIsSet foo; echo "Still running." )
foo is not set, aborting.
lhunath
DaveP
+4  A: 

Thanks to lhunath's answer, I was led to a part of the Bash man page that I've overlooked hundreds of times:

    When  not performing substring  expansion, bash tests for a parameter that
    is unset  or null; omitting the colon results in a test only for a parame‐
    ter that is unset.

This prompted me to create the following truth table:


                | unset |   set    | set and  | meaning
                |       | but null | not null |
    ============+=======+==========+==========+=============================
     ${var-_}   |   T   |     F    |    T     | not null or not set
    ------------+-------+----------+----------+-----------------------------
     ${var:-_}  |   T   |     T    |    T     | always true, use for subst.
    ------------+-------+----------+----------+-----------------------------
     $var       |   F   |     F    |    T     | var is set and not null
    ------------+-------+----------+----------+-----------------------------
     ${!var[@]} |   F   |     T    |    T     | var is set

This table introduces the specification in the last row. The Bash man page says "If name is not an array, expands to 0 if name is set and null otherwise." For purposes of this truth table, it behaves the same even if it's an array.

Dennis Williamson