In modern bash (version 4.2 and above):
[[ -v name_of_var ]]
From help test:
Answer from Chris Down on Stack Exchange-v VAR, True if the shell variable VAR is set
In modern bash (version 4.2 and above):
[[ -v name_of_var ]]
From help test:
-v VAR, True if the shell variable VAR is set
Depends what you mean by exists.
Does a variable that has been declared but not assigned exist?
Does an array (or hash) variable that has been assigned an empty list exist?
Does a nameref variable pointing to a variable that currently isn't assigned exist?
Do you consider $-, $#, $1 variables? (POSIX doesn't).
In Bourne-like shells, the canonical way is:
if [ -n "${var+set}" ]; then
echo '$var was set'
fi
That works for scalar variables and other parameters to tell if a variable has been assigned a value (empty or not, automatically, from the environment, assigments, read, for or other).
For shells that have a typeset or declare command, that would not report as set the variables that have been declared but not assigned (note that in zsh, declaring a variable assigns a value, a default one if not specified).
For shells that support arrays, except for yash and zsh that would not report as set array variables unless the element of indice 0 has been set.
For bash (but not ksh93 nor zsh), for variables of type associative array, that would not report them as set unless their element of key "0" has been set.
For ksh93 and bash, for variables of type nameref, that only returns true if the variable referenced by the nameref is itself considered set.
For ksh, zsh and bash, a potentially better approach could be:
if ((${#var[@]})); then
echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi
For ksh93, zsh and bash 4.4 or above, there's also:
if typeset -p var 2> /dev/null | grep -q '^'; then
echo '$var exists'
fi
Which will report variables that have been set or declared.
(Usually) The right way
if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
where ${var+x} is a parameter expansion which evaluates to nothing if var is unset, and substitutes the string x otherwise.
Quotes Digression
Quotes can be omitted (so we can say ${var+x} instead of "${var+x}") because this syntax & usage guarantees this will only expand to something that does not require quotes (since it either expands to x (which contains no word breaks so it needs no quotes), or to nothing (which results in [ -z ], which conveniently evaluates to the same value (true) that [ -z "" ] does as well)).
However, while quotes can be safely omitted, and it was not immediately obvious to all (it wasn't even apparent to the first author of this quotes explanation who is also a major Bash coder), it would sometimes be better to write the solution with quotes as [ -z "${var+x}" ], at the very small possible cost of an O(1) speed penalty. The first author also added this as a comment next to the code using this solution giving the URL to this answer, which now also includes the explanation for why the quotes can be safely omitted.
(Often) The wrong way
if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi
This is often wrong because it doesn't distinguish between a variable that is unset and a variable that is set to the empty string. That is to say, if var='', then the above solution will output "var is blank".
The distinction between unset and "set to the empty string" is essential in situations where the user has to specify an extension, or additional list of properties, and that not specifying them defaults to a non-empty value, whereas specifying the empty string should make the script use an empty extension or list of additional properties.
The distinction may not be essential in every scenario though. In those cases [ -z "$var" ] will be just fine.
To check for non-null/non-zero string variable, i.e. if set, use
if [ -n "$1" ]
It's the opposite of -z. I find myself using -n more than -z.
You would use it like:
if [ -n "$1" ]; then
echo "You supplied the first parameter!"
else
echo "First parameter not supplied."
fi
Such a test does not exist as it might in a high-level language such as Java or C#, but it is relatively easy to construct.
Three comments on your question as asked:
1: You do not work with literal "true"/"false" values in bash; all commands and conditional constructs yield an exit status, or logical result, which is either 0 (true) or non-0 (false).
Work with this, don't try to re-invent the wheel.
2: Try not to use upper-case parameter names unless they are environment variables; this is a simple convention that helps keep your code clean.
3: What, exactly, do you mean by "NULL"? The literal string NULL, an empty value, or something else?
The latter two concepts don't exist in bash; a NULL variable would be unset, which you're already checking for.
The most effective solution would be a function that accepts a variable (or parameter, as bash calls them), and checks for those conditions, then sets the exit status based on that, effectively condensing multiple truth values to a literal 0 or 1.
For example:
is_true() { if [[ "$1" = 0 ]]; then false; elif [[ -z "$1" ]]; then false; elif [[ "$1" =~ [Ff][Aa][Ll][Ss][Ee] ]]; then false; fi; }
is_true "1"yields 0, which is trueis_true "false"yields 1, which is falsea=10; is_true "$a"yields 0grep -q something somefileyields 0 ifsomethingappears at least once insomefile.
NOTE that the latter example clearly shows that you don't NEED such a construct; everything that happens in bash already works with these basic 0/1 truth values.
But I'll grant that you might have to convert input from some gnarly external program :)
I find these comparisons unuseful because they make appearance of code obscur and unobvious. I just use the nice feature of bash, that comes since v4.2, from the manpage:
-v varname
True if the shell variable varname is set (has been assigned a value).
Usage is very simple
OUTPUT_TO_LOG=1
if [ -v OUTPUT_TO_LOG ]; then
exec > &>./logfile
…
fi
In other words, to flag as enabled, just define. Try it!
$ :() { if [ -v VAR ]; then echo 'existed'; fi }
$ unset VAR && : # no output
$ unset VAR && VAR= && : # "existed"
$ unset VAR && VAR=1 && : # "existed"
This is what I've found works best for me, taking inspiration from the other answers:
if [ -z "${varname-}" ]; then
...
varname=$(...)
fi
What Doesn't Work: Test for Zero-Length Strings
You can test for undefined strings in a few ways. Using the standard test conditional looks like this:
# Test for zero-length string.
[ -z "$variable" ] || variable='foo'
This will not work with set -u, however.
What Works: Conditional Assignment
Alternatively, you can use conditional assignment, which is a more Bash-like way to do this. For example:
# Assign value if variable is unset or null.
: "${variable:=foo}"
Because of the way Bash handles expansion of this expression, you can safely use this with set -u without getting a "bash: variable: unbound variable" error.
Currently i have a bash script which checks if the variable VARIABLE_ONE is set, and if not spills out an error. The problem is I don't understand how it works :
set -e
: ${VARIABLE_ONE:?"error: variable is not set or empty."}What does the ":" at the start do ? And the ternary operator inside the curly brackets ?
I think the answer you are after is implied (if not stated) by Vinko's answer, though it is not spelled out simply. To distinguish whether VAR is set but empty or not set, you can use:
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
You probably can combine the two tests on the second line into one with:
if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
However, if you read the documentation for Autoconf, you'll find that they do not recommend combining terms with '-a' and do recommend using separate simple tests combined with &&. I've not encountered a system where there is a problem; that doesn't mean they didn't used to exist (but they are probably extremely rare these days, even if they weren't as rare in the distant past).
You can find the details of these, and other related shell parameter expansions, the test or [ command and conditional expressions in the Bash manual.
I was recently asked by email about this answer with the question:
You use two tests, and I understand the second one well, but not the first one. More precisely I don't understand the need for variable expansion
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fiWouldn't this accomplish the same?
if [ -z "${VAR}" ]; then echo "VAR is not set at all"; fi
Fair question - the answer is 'No, your simpler alternative does not do the same thing'.
Suppose I write this before your test:
VAR=
Your test will say "VAR is not set at all", but mine will say (by implication because it echoes nothing) "VAR is set but its value might be empty". Try this script:
(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo "JL:1 VAR is not set at all"; fi
if [ -z "${VAR}" ]; then echo "MP:1 VAR is not set at all"; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo "JL:2 VAR is not set at all"; fi
if [ -z "${VAR}" ]; then echo "MP:2 VAR is not set at all"; fi
)
The output is:
JL:1 VAR is not set at all MP:1 VAR is not set at all MP:2 VAR is not set at all
In the second pair of tests, the variable is set, but it is set to the empty value. This is the distinction that the ${VAR=value} and ${VAR:=value} notations make. Ditto for ${VAR-value} and ${VAR:-value}, and ${VAR+value} and ${VAR:+value}, and so on.
As Gili points out in his answer, if you run bash with the set -o nounset option, then the basic answer above fails with unbound variable. It is easily remedied:
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
Or you could cancel the set -o nounset option with set +u (set -u being equivalent to set -o nounset).
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~>
-z works for undefined variables too. To distinguish between an undefined and a defined you'd use the things listed here or, with clearer explanations, here.
Cleanest way is using expansion like in these examples. To get all your options check the Parameter Expansion section of the manual.
Alternate word:
~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED
Default value:
~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED
Of course you'd use one of these differently, putting the value you want instead of 'default value' and using the expansion directly, if appropriate.
Use parameter expansion:
: ${var:?}
Remove the colon if the empty string is a valid value (i.e., you only want to test for definedness).
: ${var?}
If you don't want the script to stop on the problem, you can use
if [[ ${var:+1} ]] ; then
# OK...
else
echo Variable empty or not defined. >&2
fi
Documented under Parameter Expansion in man bash:
When not performing substring expansion, using the forms documented below (e.g.,
:-), bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset.
${parameter:?word}Display Error if Null or Unset. If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.
${parameter:+word}Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
You probably want to use indirect expansion: ${!variable} and then -n to check if it has been defined:
The indirect expansion consists in calling a variable with another variable. That is, as the variable name may be changing, instead of saying $a we say ${!var} and var=a.
$ cat a
var1_ID=0x04
var2_ID=0x05
var3_ID=0x06
var4_ID=0x09
for i in {1..5}; do
v="var${i}_ID"
if [ -n "${!v}" ]; then # <-- this expands to varX_ID
echo "$v set to value: ${!v}"
else
echo "$v not set"
fi
done
If we execute, we get:
$ ./a
var1_ID set to value: 0x04
var2_ID set to value: 0x05
var3_ID set to value: 0x06
var4_ID set to value: 0x09
var5_ID not set
From man test:
-n STRING
the length of STRING is nonzero
Portable to all POSIX shells:
if [ -n "${foobar+1}" ]; then
echo "foobar is defined"
else
echo "foobar is not defined"
fi
Make that ${foobar:+1} if you want to treat foobar the same way whether it is empty or not defined. You can also use ${foobar-} to get an empty string when foobar is undefined and the value of foobar otherwise (or put any other default value after the -).
In ksh, if foobar is declared but not defined, as in typeset -a foobar, then ${foobar+1} expands to the empty string.
Zsh doesn't have variables that are declared but not set: typeset -a foobar creates an empty array.
In bash, arrays behave in a different and surprising way. ${a+1} only expands to 1 if a is a non-empty array, e.g.
typeset -a a; echo ${a+1} # prints nothing
e=(); echo ${e+1} # prints nothing!
f=(''); echo ${f+1} # prints 1
The same principle applies to associative arrays: array variables are treated as defined if they have a non-empty set of indices.
A different, bash-specific way of testing whether a variable of any type has been defined is to check whether it's listed in ${!PREFIX*}. This reports empty arrays as defined, unlike ${foobar+1}, but reports declared-but-unassigned variables (unset foobar; typeset -a foobar) as undefined.
case " ${!foobar*} " in
*" foobar "*) echo "foobar is defined";;
*) echo "foobar is not defined";;
esac
This is equivalent to testing the return value of typeset -p foobar or declare -p foobar, except that typeset -p foobar fails on declared-but-unassigned variables.
In bash, like in ksh, set -o nounset; typeset -a foobar; echo $foobar triggers an error in the attempt to expand the undefined variable foobar. Unlike in ksh, set -o nounset; foobar=(); echo $foobar (or echo "${foobar[@]}") also triggers an error.
Note that in all situations described here, ${foobar+1} expands to the empty string if and only if $foobar would cause an error under set -o nounset.
To sum up with Gilles' answer I made up my following rules:
- Use
[[ -v foobar ]]for variables in Bash version >= 4.2. - Use
declare -p foobar &>/dev/nullfor array variables in Bash version < 4.2. - Use
(( ${foo[0]+1} ))or(( ${bar[foo]+1} ))for subscripts of indexed (-a) and keyed (-A) arrays (declare), respectively. Options 1 and 2 don't work here.
This will return true if a variable is unset or set to the empty string ("").
if [ -z "${VAR}" ];
In Bash, when you're not concerned with portability to shells that don't support it, you should always use the double-bracket syntax:
Any of the following:
if [[ -z $variable ]]
if [[ -z "$variable" ]]
if [[ ! $variable ]]
if [[ ! "$variable" ]]
In Bash, using double square brackets, the quotes aren't necessary. You can simplify the test for a variable that does contain a value to:
if [[ $variable ]]
This syntax is compatible with ksh (at least ksh93, anyway). It does not work in pure POSIX or older Bourne shells such as sh or dash.
See my answer here and BashFAQ/031 for more information about the differences between double and single square brackets.
You can test to see if a variable is specifically unset (as distinct from an empty string):
if [[ -z ${variable+x} ]]
where the "x" is arbitrary.
If you want to know whether a variable is null but not unset:
if [[ -z $variable && ${variable+x} ]]
NOTE: OP stated in the comments "I'm using bash temporally in termux app. It seems to behave differently, than 'regular' bash." This explains the unusual behavior.
According to bash 4.3 manual used in Ubuntu 16.04:
Each variable assignment is checked for unquoted tilde-prefixes immediately following a : or the first =. In these cases, tilde expanion is also performed.
In your specific case, tilde is treated as literal character, and your [ is looking for file in directory that is literally supposed to be called ~. That's why it fails.
Use environment variable $HOME instead, i.e. $HOME/tost1.
This works for me in 16.04 LTS, when run from the command line in a terminal window,
$ i=~/tost1
$ echo "$i"
/home/sudodus/tost1
$ touch ~/tost1
$ if [ -e ${i} ]; then echo "file exists";else echo "file does not exist";fi
file exists
But it is a good idea to use $HOME instead of tilde, particularly if you intend to make a shellscript with the commands that you test interactively, so start with
$ i="$HOME"/tost1
$ touch ~/tost1
$ if [ -e "${i}" ]; then echo "file exists";else echo "file does not exist";fi
file exists
It is a good habit to double-quote variables, if you want to avoid unpleasant surprises. (There are a few exceptions.)