Bash function to check if a given variable is set - bash

As explained in this answer, the "right" way to check if a variable is set in bash looks like this:
if [ -z ${var+x} ]; then
echo "var is unset"
else
echo "var is set to '$var'"
fi
What I'm interested in is how to extract this into a function that can be reused for different variables.
The best I've been able to do so far is:
is_set() {
local test_start='[ ! -z ${'
local test_end='+x} ]'
local tester=$test_start$1$test_end
eval $tester
}
It seems to work, but is there a better way that doesn't resort to calling eval?

In Bash you can use [[ -v var ]]. There's no need for a function or convoluted schemes.
From the manpage:
-v varname
True if the shell variable varname is set (has been assigned a value).
The first 2 command sequences prints ok:
[[ -v PATH ]] && echo ok
var="" ; [[ -v var ]] && echo ok
unset var ; [[ -v var ]] && echo ok

With [[ ... ]], you can simply do this (there is no need for double quotes):
[[ $var ]] && echo "var is set"
[[ $var ]] || echo "var is not set or it holds an empty string"
If you really want a function, then we can write one-liners:
is_empty() { ! [[ $1 ]]; } # check if not set or set to empty string
is_not_empty() { [[ $1 ]]; } # check if set to a non-empty string
var="apple"; is_not_empty "$var" && echo "var is not empty" # shows "var is not empty"
var="" ; is_empty "$var" && echo "var is empty" # shows "var is empty"
unset var ; is_empty "$var" && echo "var is empty" # shows "var is empty"
var="apple"; is_empty "$var" || echo "var is not empty" # shows "var is not empty"
Finally, is_unset and is_set could be implemented to treat $1 as the name of the variable:
is_unset() { [[ -z "${!1+x}" ]]; } # use indirection to inspect variable passed through $1 is unset
is_set() { ! [[ -z "${!1+x}" ]]; } # check if set, even to an empty string
unset var; is_unset var && echo "var is unset" # shows "var is unset"
var="" ; is_set var && echo "var is set" # shows "var is set"
var="OK" ; is_set var && echo "var is set" # shows "var is set"
Related
Test for non-zero length string in Bash: [ -n “$var” ] or [ “$var” ]

Related

Check if variables exist in a for loop in bash

I need to check a lot of environment variables that need to be set in order for my bash script to run. I've seen this question and tried
thisVariableIsSet='123'
variables=(
$thisVariableIsSet
$thisVariableIsNotSet
)
echo "check with if"
# this works
if [[ -z ${thisVariableIsNotSet+x} ]]; then
echo "var is unset";
else
echo "var is set to '$thisVariableIsNotSet'";
fi
echo "check with for loop"
# this does not work
for variable in "${variables[#]}"
do
if [[ -z ${variable+x} ]]; then
echo "var is unset";
else
echo "var is set to '$variable'";
fi
done
The output is:
mles:tmp mles$ ./test.sh
check with if
var is unset
check with for loop
var is set to '123'
If I'm checking the not set variable in an if block, the check works (var is unset). However in the for loop the if block only prints if a variable is set, not if a variable ist not set.
How can I check variables in a for loop?
You can try to use indirect expansion ${!var}:
thisVariableIsSet='123'
variables=(
thisVariableIsSet # no $
thisVariableIsNotSet
)
echo "check with if"
# this works
if [[ -z ${thisVariableIsNotSet+x} ]]; then
echo "var is unset";
else
echo "var is set to '$thisVariableIsNotSet'";
fi
echo "check with for loop"
# this does not work
for variable in "${variables[#]}"
do
if [[ -z ${!variable+x} ]]; then # indirect expansion here
echo "var is unset";
else
echo "var is set to ${!variable}";
fi
done
Output:
check with if
var is unset
check with for loop
var is set to 123
var is unset

existing empty null variable in bash

I am using the code below to determine if a variable in bash exists, if it is empty, or if it has length>0. The code works, but I can't find a good explanation for how if [ -n "${emptyvar+1}" ] can detect if emptyvar is not set. If I remove the +1 then the test fails for "". What is the purpose of the +1 in the test?
#!/bin/bash
emptyvar="a"
if [ -n "${emptyvar+1}" ]
then
echo "emptyvar is defined"
if [[ -z $emptyvar ]]
then
echo "emptyvar is empty";
else
echo "emptyvar is NOT empty";
if [[ -n $emptyvar ]]
then
echo "emptyvar has length > 0";
else
echo "emptyvar has length 0";
fi
fi
else
echo "emptyvar is not defined"
fi
From the bash documentation of Shell Parameter Expansion:
${parameter:+word} If parameter is null or unset, nothing is
substituted, otherwise the expansion of word is substituted.
Omitting the colon (:) makes it test only if the variable is unset, rather than null or unset.
So ${emptyvar+1} tests if $emptyvar is unset. If it is, it expands to the empty string; if not, it expands to 1.
You can also create sets of functions to test a variable passed by its name:
function is_var_set {
[[ -n ${!1+.} ]]
}
function is_var_empty {
[[ -z ${!1} ]]
}
Test:
> A=''
> is_var_set A && echo "A is set." || echo "A is unset."
A is set.
> is_var_empty A && echo "A is empty." || echo "A is not empty."
A is empty.
bash 4.2 added a new test operator, -v, that tests if a variable has been set.
# -v takes the name of the variable, not its values (since you are
# testing if it has a value or not).
if [[ -v emptyvar ]]; then
echo "emptyvar is defined"
if [[ -z $emptyvar ]]
then
echo "emptyvar is empty";
else
echo "emptyvar is NOT empty";
fi
else
echo "emptyvar is not defined"
fi
Note that an empty variable is one whose string has length 0, so your -n test is redundant.
Another way to express it:
$ unset var
$ if [[ -z ${var+x} ]]; then echo unset; elif [[ -z $var ]]; then echo empty; else echo not empty; fi
unset
$ var=
$ if [[ -z ${var+x} ]]; then echo unset; elif [[ -z $var ]]; then echo empty; else echo not empty; fi
empty
$ var=foo
$ if [[ -z ${var+x} ]]; then echo unset; elif [[ -z $var ]]; then echo empty; else echo not empty; fi
not empty
You'll never get "emptyvar has length 0" -- that's what "empty" is

Bash Boolean testing

I am attempting to run a block of code if one flag is set to true and the other is set to false. ie
var1=true
var2=false
if [[ $var1 && ! $var2 ]]; then var2="something"; fi
Since that did not evaluate the way that I expected I wrote several other test cases and I am having a hard time understanding how they are being evaluated.
aa=true
bb=false
cc="python"
if [[ "$aa" ]]; then echo "Test0" ; fi
if [[ "$bb" ]]; then echo "Test0.1" ; fi
if [[ !"$aa" ]]; then echo "Test0.2" ; fi
if [[ ! "$aa" ]]; then echo "Test0.3" ; fi
if [[ "$aa" && ! "$bb" ]]; then echo "Test1" ; fi
if [[ "$aa" && ! "$aa" ]]; then echo "Test2" ; fi
if [[ "$aa" ]] && ! [[ "$bb" ]]; then echo "test3" ; fi
if [[ "$aa" ]] && ! [[ "$cc" ]]; then echo "test4" ; fi
if [[ $aa && ! $bb ]]; then echo "Test5" ; fi
if [[ $aa && ! $aa ]]; then echo "Test6" ; fi
if [[ $aa ]] && ! [[ $bb ]]; then echo "test7" ; fi
if [[ $aa ]] && ! [[ $cc ]]; then echo "test8" ; fi
When I run the preceding codeblock the only output I get is
Test0
Test0.1
Test0.2
however, my expectation is that I would get
Test0
Test1
Test3
Test5
Test7
I have tried to understand the best way to run similar tests, however most examples I have found are set up in the format of
if [[ "$aa" == true ]];
which is not quite what I want to do. So my question is what is the best way to make comparisons like this, and why do several of the test cases that I would expect to pass simply not?
Thank you!
Without any operators, [[ only checks if the variable is empty. If it is, then it is considered false, otherwise it is considered true. The contents of the variables do not matter.
Your understanding of booleans in shell context is incorrect.
var1=true
var2=false
Both the above variables are true since those are non-empty strings.
You could instead make use of arithmetic context:
$ a=1
$ b=0
$ ((a==1 && b==0)) && echo y
y
$ ((a==0 && b==0)) && echo y
$
$ ((a && !(b))) && echo y; # This seems to be analogous to what you were attempting
y
The shell does not have Boolean variables, per se. However, there are commands named true and false whose exit statuses are 0 and 1, respectively, and so can be used similarly to Boolean values.
var1=true
var2=false
if $var1 && ! $var2; then var2="something"; fi
The difference is that instead of testing if var1 is set to a true value, you expand it to the name of a command, which runs and succeeds. Likewise, var2 is expanded to a command name which runs and fails, but because it is prefixed with ! the exit status is inverted to indicate success.
(Note that unlike most programming languages, an exit status of 0 indicates success because while most commands have 1 way to succeed, there are many different ways they could fail, so different non-zero values can be assigned different meanings.)
true and false are evaluated as strings ;)
[[ $var ]] is an equivalent of [[ -n $var ]] that check if $var is empty or not.
Then, no need to quote your variables inside [[. See this reminder.
Finally, here is an explication of the difference between && inside brackets and outside.
The closest you can come seems to be use functions instead of variables because you can use their return status in conditionals.
$ var1() { return 0; }
$ var2() { return 1; } # !0 = failure ~ false
and we can test this way
$ var1 && echo "it's true" || echo "it's false"
it's true
$ var2 && echo "it's true" || echo "it's false"
it's false
or this way
$ if var1; then echo "it's true"; else echo "it's false"; fi
it's true
$ if var2; then echo "it's true"; else echo "it's false"; fi
it's false
Hope this helps.

Regarding Bash substring comparison

I try to test if a string starts with a certain prefix. But my script seems not work (I would expect the "if" branch will not get run). Can some Bash expert help to take a look? thanks!
Here is my code and test result:
$ cat testb.bash
#!/bin/bash
my_var="abcdefg";
if [[ "${my_var:0:5}"=="order" ]]; then
echo "value of my_var is ${my_var}.";
fi;
if [[ "${my_var:0:5}" -eq "order" ]]; then
echo "value of my_var is ${my_var}.";
fi;
if [ "${my_var:0:5}"="order" ]; then
echo "value of my_var is ${my_var}.";
fi;
$ bash -x testb.bash
+ my_var=abcdefg
+ [[ -n abcde==order ]]
+ echo 'value of my_var is abcdefg.'
value of my_var is abcdefg.
+ [[ abcde -eq order ]]
+ echo 'value of my_var is abcdefg.'
value of my_var is abcdefg.
+ '[' abcde=order ']'
+ echo 'value of my_var is abcdefg.'
value of my_var is abcdefg.
$
Whitespace is significant in this case. As you can see in the -x output, it understands the first condition as
[[ -n "${my_var:0:5}==order" ]]
Moreover, to test for a prefix, you can use a pattern:
[[ $my_var == order* ]]
To test the existence of substring, you can use either of these:
if [[ "$j" =~ string1 ]]; then
if [[ $j == *string1* ]]; then
In your particular case, you miss a space surounding ==, so instead of
if [[ "${my_var:0:5}"=="order" ]]; then
it should be
if [[ "${my_var:0:5}" == "order" ]]; then
^ ^
Finally, note that your condition was evaluated as true because it was evaluating if [ "string" ], which is true if string is not empty:
$ [ "a" ] && echo "yes"
yes
Test
$ cat a
#!/bin/bash
my_var="abcdefg";
if [[ "${my_var:0:5}" == "order" ]]; then
echo "value of my_var is ${my_var}."
elif [[ "${my_var:0:5}" == "abcde" ]]; then
echo "yeahaa"
else
echo "is not"
fi
$ ./a
yeahaa
Ok, i tested your code, you shoud such as the following code:
prefix="pre_order";
pre="pre_"
len=${#pre}
echo $len
if [[ "${prefix:0:len}" == "blahvlah" ]] ; then
echo "dddd"
fi;
Notes:
use == for string comparation
for ${} you should initilize a string variable before ${}
use len=${#pre} for lenght of string.
A POSIX-compliant way to test for a prefix is to attempt to remove the prefix, and compare the result to the original string. If the two are the same, the prefix is not present, the removal fails, and the expression expands to the original string.
prefix=foo
string=foobar
if [ "${string#$prefix}" = "$string" ]; then
printf "$string does not start with $prefix\n"
else
printf "$string starts with $prefix\n"
fi

How to find whether or not a variable is empty in Bash

How can I check if a variable is empty in Bash?
In Bash at least the following command tests if $var is empty:
if [[ -z "$var" ]]; then
# $var is empty, do what you want
fi
The command man test is your friend.
Presuming Bash:
var=""
if [ -n "$var" ]; then
echo "not empty"
else
echo "empty"
fi
I have also seen
if [ "x$variable" = "x" ]; then ...
which is obviously very robust and shell independent.
Also, there is a difference between "empty" and "unset". See How to tell if a string is not defined in a Bash shell script.
if [ ${foo:+1} ]
then
echo "yes"
fi
prints yes if the variable is set. ${foo:+1} will return 1 when the variable is set, otherwise it will return empty string.
[ "$variable" ] || echo empty
: ${variable="value_to_set_if_unset"}
if [[ "$variable" == "" ]] ...
The question asks how to check if a variable is an empty string and the best answers are already given for that.
But I landed here after a period passed programming in PHP, and I was actually searching for a check like the empty function in PHP working in a Bash shell.
After reading the answers I realized I was not thinking properly in Bash, but anyhow in that moment a function like empty in PHP would have been soooo handy in my Bash code.
As I think this can happen to others, I decided to convert the PHP empty function in Bash.
According to the PHP manual:
a variable is considered empty if it doesn't exist or if its value is one of the following:
"" (an empty string)
0 (0 as an integer)
0.0 (0 as a float)
"0" (0 as a string)
an empty array
a variable declared, but without a value
Of course the null and false cases cannot be converted in bash, so they are omitted.
function empty
{
local var="$1"
# Return true if:
# 1. var is a null string ("" as empty string)
# 2. a non set variable is passed
# 3. a declared variable or array but without a value is passed
# 4. an empty array is passed
if test -z "$var"
then
[[ $( echo "1" ) ]]
return
# Return true if var is zero (0 as an integer or "0" as a string)
elif [ "$var" == 0 2> /dev/null ]
then
[[ $( echo "1" ) ]]
return
# Return true if var is 0.0 (0 as a float)
elif [ "$var" == 0.0 2> /dev/null ]
then
[[ $( echo "1" ) ]]
return
fi
[[ $( echo "" ) ]]
}
Example of usage:
if empty "${var}"
then
echo "empty"
else
echo "not empty"
fi
Demo:
The following snippet:
#!/bin/bash
vars=(
""
0
0.0
"0"
1
"string"
" "
)
for (( i=0; i<${#vars[#]}; i++ ))
do
var="${vars[$i]}"
if empty "${var}"
then
what="empty"
else
what="not empty"
fi
echo "VAR \"$var\" is $what"
done
exit
outputs:
VAR "" is empty
VAR "0" is empty
VAR "0.0" is empty
VAR "0" is empty
VAR "1" is not empty
VAR "string" is not empty
VAR " " is not empty
Having said that in a Bash logic the checks on zero in this function can cause side problems imho, anyone using this function should evaluate this risk and maybe decide to cut those checks off leaving only the first one.
This will return true if a variable is unset or set to the empty string ("").
if [ -z "$MyVar" ]
then
echo "The variable MyVar has nothing in it."
elif ! [ -z "$MyVar" ]
then
echo "The variable MyVar has something in it."
fi
You may want to distinguish between unset variables and variables that are set and empty:
is_empty() {
local var_name="$1"
local var_value="${!var_name}"
if [[ -v "$var_name" ]]; then
if [[ -n "$var_value" ]]; then
echo "set and non-empty"
else
echo "set and empty"
fi
else
echo "unset"
fi
}
str="foo"
empty=""
is_empty str
is_empty empty
is_empty none
Result:
set and non-empty
set and empty
unset
BTW, I recommend using set -u which will cause an error when reading unset variables, this can save you from disasters such as
rm -rf $dir
You can read about this and other best practices for a "strict mode" here.
To check if variable v is not set
if [ "$v" == "" ]; then
echo "v not set"
fi

Resources