How do I check if a variable exists in an 'if' statement?

279,888

Solution 1

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

Solution 2

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.

Solution 3

As mentioned in the answer on SO, here is a way to check:

if [ -z ${somevar+x} ]; then echo "somevar is unset"; else echo "somevar is set to '$somevar'"; fi

where ${somevar+x} is a parameter expansion which evaluates to the null if var is unset and substitutes the string "x" otherwise.

Using -n, as suggested by the other answer, will only check if the variable contains empty string. It will not check its existence.

Solution 4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

or you can let your shell show the message for you:

(: "${somevar?}")
zsh: somevar: parameter not set

Solution 5

if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi
Share:
279,888

Related videos on Youtube

Thaddaeus Markle
Author by

Thaddaeus Markle

Updated on September 18, 2022

Comments

  • Thaddaeus Markle
    Thaddaeus Markle almost 2 years

    I need to check a variable's existence in an if statement. Something to the effect of:

    if [ -v $somevar ]
    then
        echo "Variable somevar exists!"
    else
        echo "Variable somevar does not exist!"
    

    And the closest question to that was this, which doesn't actually answer my question.

    • Admin
      Admin about 9 years
      If you want to set $somevar to a value/string if variable does not exist: ${somevar:=42}.
    • Admin
      Admin over 8 years
      Personally, I tend to check just for emptiness ([ -n "$var" ] or [ ! -z "$var" ]). I think the existence/nonexistence checks are too subtle, and I prefer my code coarse and simple.
    • Admin
      Admin about 8 years
      Why do you need that vs [ -n "$var" ]? Related: stackoverflow.com/questions/3601515/…
  • meuh
    meuh about 9 years
    Also works with single brackets: [ -v name_of_var ].
  • mikeserv
    mikeserv about 9 years
    You need to quote $somevar to handle IFS=x. Either that or quote x.
  • cuonglm
    cuonglm about 9 years
    @mikeserv: Yeah, of course, there's many way to do it. Firstly, I think I will duplicated it with this. But in this question, the OP want to check only, he didn't claim that he want to exit or report if variable unset, so I came with a check in subshell.
  • mikeserv
    mikeserv about 9 years
    Well, i know, but it's precisely because you have to do it in a subshell like that which indicates it might not be the best way to test here - that's a halt action on failure, it doesn't allow for any simple means to handle a failure. Portably even a trap can only work on EXIT. That's all I'm saying - it just doesn't apply as a pass/fail very well. And this isn't me talking either - i've done exactly this before and it took a little comment chat just like this to convince me. So, I just thought I'd pay it forward.
  • cuonglm
    cuonglm about 9 years
    @mikeserv: Well, well, it's a good point. I only wonder, does adding that can make the OP confuse with the syntax :)
  • Stéphane Chazelas
    Stéphane Chazelas over 8 years
    beware that for hashes and arrays, it returns false unless the variable has an element of key/indice "0". For namerefs, it tests whether the target is defined. It doesn't work for special parameters like $1, $-, $#...
  • Admin
    Admin over 8 years
    Yes, except in some old versions of zsh in sh emulation when $IFS contains f, a, l, s or e. Like for other answers, there's the case of arrays, hashes, or other types of variables which one may want to mention.
  • Alessio
    Alessio about 8 years
    declare -p / typeset -p works in bash now too.
  • Stéphane Chazelas
    Stéphane Chazelas about 8 years
    @cas, no. Not for declared but not set variables. Try bash -c 'typeset -i a; typeset -p a' and compare with ksh93 or zsh.
  • Tim
    Tim about 8 years
    +1 Thanks for point me here. Also see my question unix.stackexchange.com/q/280893/674
  • Admin
    Admin about 8 years
    @StéphaneChazelas Also: The question title is very specific: Bash.
  • Mark Lakata
    Mark Lakata almost 8 years
    This feature is only in the bash builtin test or [ ; It is not available in /usr/bin/test. Compare man test with help test.
  • clerksx
    clerksx almost 8 years
    @MarkLakata Right, because external commands cannot know the internal state of the shell.
  • Alexander Mills
    Alexander Mills over 7 years
    umm shouldn't it always be [[ -v "$name_of_var" ]] ?
  • clerksx
    clerksx over 7 years
    @AlexanderMills No. That would if the variable name in $name_of_var existed. Try it for yourself.
  • Stéphane Chazelas
    Stéphane Chazelas over 6 years
    @cas, that changed with bash-4.4 (released in September 2016), I've edited that in.
  • Tom Hale
    Tom Hale almost 6 years
    Also works in zsh from version 5.3+
  • Tom Hale
    Tom Hale almost 6 years
    @mikeserv Thanks, I like learning about edge cases :) Do you mean if [ -z "${somevar+x}" ]? Would the quoting still be required inside [[ and ]]?
  • Tom Hale
    Tom Hale almost 6 years
    Fails when variable set to null string.
  • mikeserv
    mikeserv over 5 years
    @TomHale - yes, in rare cases. the [ test routines accept command line parameters, and so the usual expansions and interpretations as ordered in the usual way should be relied upon to render at invocation of the test applied that which you should cause to be read thereby by any program command line. test{!+"!"}
  • Tom Hale
    Tom Hale over 5 years
    @mikeserv I guess your yes is to my 2nd question... Is the first a yes, too?
  • Admin
    Admin over 5 years
    binary zebra???
  • mikeserv
    mikeserv over 5 years
    @Tom Hale - to my my way of thinking, [ -z, if applied at all to a parameter check, is most useful as a corner case standin. the not applicable corollary... [ ${*?cant handle that, bash? or do the prior bournes slip at :? theres a test for that, too, if your word is worth the encoding... save for later? freezers are cool} and plus works for almost anything (stay positive, everybody) [ ${-+"-z"} $- ]&& now what? of course, by design, file system glo[bal]{1,2}ing might not be so confusing as a possible forty-two deep link back check... fortuitous? thats quite a lot.
  • Tom Hale
    Tom Hale over 5 years
    Sorry @mikeserv, I have no idea what you mean... would you mind to try again with more simple examples?
  • Kyle Strand
    Kyle Strand over 5 years
    +1; this appears to be the simplest way to do this when set -u is in effect and the Bash version is pre-4.2.
  • Ilias ETTOUKI
    Ilias ETTOUKI almost 5 years
    Not sure why it's an accepted answer. OP did not limit shell to bash only.
  • Priyanshu Dhal
    Priyanshu Dhal almost 5 years
    I would imagine this to be somewhat inefficient, but it is very simple and sh compatible, which is just what I need.
  • antichris
    antichris about 4 years
    Don't check the length (the -n flag for test) in POSIX. Simply do [ "${var+.}" ]. The plus expansion substitutes unset to null string and test evaluates true if string is not the null string, thus the entire expression evaluates to false only when $var is unset.
  • Stéphane Chazelas
    Stéphane Chazelas about 4 years
    @antichris, that's a matter of taste. I do prefer the [ -n string ] over [ string ] as it makes it clearer what is being tested as it follows the usual [ -operator operand ] pattern.
  • antichris
    antichris about 4 years
    +1 This should be the top answer.
  • Stéphane Chazelas
    Stéphane Chazelas about 4 years
    @antichris, that's hardly relevant, in both cases it's a builtin call. You might save 10% of a few hundred nanosecond calls with some implementation when a shell's job is to run commands which take at least a few miliseconds to run. I don't want to be drawn into this kind of micro-optimisations when that diminishes legibility. [ -z ], [ -n ] come together for null and non-null string tests. I'm not going to replace [ -z "$var" ] with [ ! "$var" ] or [ "$#" -ne 0 ] with [ "${1+x}" ], we'll have to agree to disagree on our priorities.
  • antichris
    antichris about 4 years
    @StéphaneChazelas [ "${var+.}" ] is shorter, sweeter and faster (and those factors become significant, for example, when dropping a shellcode). Just saying. But you do you.
  • RichieHH
    RichieHH over 3 years
    @antichris no it isn't for that vast majority. It's shorter and sweeter for experts that revel in such things, I do. But its not at all legible to anyone trying to understand a script when they are not experts. -n is easily googled and checked. Nobody cares about micro optimisations in a script that might be called once per session and, in any case, you could argue the compiler/interpreter would optimise it anyway.
  • antichris
    antichris over 3 years
    @RichieHH you almost got it, it's just the other way around. When I first saw the [ -n "$var" ] idiom in a script, I did indeed have to look it up (google or man), because a flag like that could mean practically anything (especially to a non-expert, inexperienced first-timer, as I was back then), while the meaning of [ "$var" ] and [ ! "$var"] has always seemed perfectly obvious to me, right from the very beginning. It's precisely the -n and -z flags that I still have to look up every once in a while to verify that I'm not mistaking one for the other.
  • Stéphane Chazelas
    Stéphane Chazelas over 3 years
    @antichris, and do you find it obvious that [ 0 ] or [ ! ] should return true? Why should [ x ] check for that very property that x be non-empty as opposed to any other property/test one could have chosen instead (is a non-zero number, is a readable file, is a know test operator...). Note that [ -t ] in some shells/tests returns true if stdin is a terminal. [[ $var ]] used not to be supported in zsh.
  • antichris
    antichris over 3 years
    @StéphaneChazelas Sure. The test command takes parameters, they have semantics and are very well documented. As for the non-emptiness check, in my experience, all languages that accept a single variable as a boolean expression have converged upon checking the "truthiness" of its value. Since POSIX shell variables can only contain (null-terminated) string values, it seems self-evident that a non-empty one must be truthy. POSIX spec has the FD number for a -t test mandatory, what you're describing is non-compliant. Double-brackets and zsh are non-POSIX so, frankly, IDGAF about either.
  • Jupiter
    Jupiter about 2 years
    @KyleStrand I usually do that with [[ -n ${MY_VAR:-} ]]
  • Kyle Strand
    Kyle Strand about 2 years
    @Jupiter Yeah, that should be equivalent, I think, though it may need quotes around the expansion.