286

I guess I'm not clear on how to do "and" tests. I wanted to make sure an argument existed which was working well with [ -e $VAR ], but it turns out that was also evaluating as true on an empty string; which I do not want.

How do I 'and' them together? Or is there another unary test that accomplishes what I want?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
atxdba
  • 5,158
  • 5
  • 24
  • 30

5 Answers5

538
if [ ! -z "$var" ] && [ -e "$var" ]; then
      # something ...
fi
jaypal singh
  • 74,723
  • 23
  • 102
  • 147
  • 10
    This solution works even in strictly POSIX-compliant shells and therefore also in `bash`; however, to take full advantage of "bashisms", see @paxdiablo's answer. – mklement0 Apr 01 '14 at 03:52
  • 2
    Very glad to see this advised instead of the obsolescent `-a`. – Charles Duffy Jun 01 '16 at 21:27
  • 1
    I found another one excellent and detailed explanation - http://stackoverflow.com/questions/3601515/how-to-check-if-a-variable-is-set-in-bash – valentt May 11 '17 at 13:17
72

From the bash manpage:

[[ expression ]] - return a status of 0 or 1 depending on the evaluation of the conditional expression expression.

And, for expressions, one of the options is:

expression1 && expression2 - true if both expression1 and expression2 are true.

So you can and them together as follows (-n is the opposite of -z so we can get rid of the !):

if [[ -n "$var" && -e "$var" ]] ; then
    echo "'$var' is non-empty and the file exists"
fi

However, I don't think it's needed in this case, -e xyzzy is true if the xyzzy file exists and can quite easily handle empty strings. If that's what you want then you don't actually need the -z non-empty check:

pax> VAR=xyzzy
pax> if [[ -e $VAR ]] ; then echo yes ; fi
pax> VAR=/tmp
pax> if [[ -e $VAR ]] ; then echo yes ; fi
yes

In other words, just use:

if [[ -e "$var" ]] ; then
    echo "'$var' exists"
fi
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
9
if [ -n "$var" -a -e "$var" ]; then
    do something ...
fi

 

Michael Thessel
  • 706
  • 5
  • 20
Slava Semushin
  • 14,904
  • 7
  • 53
  • 69
  • 10
    Since `POSIX` doesn't define the behavior of `[` with complex sets of tests, we should avoid using `-a` or `-o` with `[`. I read it [here](http://mywiki.wooledge.org/BashGuide/TestsAndConditionals?highlight=%28conditional%29#Conditional_Blocks_.28if.2C_test_and_.5B.5B.29). – jaypal singh Jan 19 '12 at 02:35
  • 2
    @jaypal-singh You are right, but topic has `bash` tag and not mention about POSIX, so I post this version which works under `bash` and some other modern shells. – Slava Semushin Jan 19 '12 at 03:40
  • 2
    If you are assuming the use of `bash` or other modern shells, there is even *less* reason to recommend `-a`. – chepner Dec 22 '14 at 23:33
  • Even with bash, the way these tests behave is not well-defined in all corner cases. Consider `[ "$var1" -o "$var2" ]`; if `var1=(` and `var2=)`, then what we have is a test for whether `-o` is non-empty, rather than whether either of `var1` or `var2` is non-empty. This kind of ambiguity is the only legitimate reason for the `x$var` idiom in modern (that is, post-90s) `test` commands with correct quoting, and that idiom needs to die in a fire. – Charles Duffy Jun 01 '16 at 21:29
4

Simply quote your variable:

[ -e "$VAR" ]

This evaluates to [ -e "" ] if $VAR is empty.

Your version does not work because it evaluates to [ -e ]. Now in this case, bash simply checks if the single argument (-e) is a non-empty string.

From the manpage:

test and [ evaluate conditional expressions using a set of rules based on the number of arguments. ...

1 argument

The expression is true if and only if the argument is not null.

(Also, this solution has the additional benefit of working with filenames containing spaces)

user123444555621
  • 148,182
  • 27
  • 114
  • 126
1

I found an answer now. Thanks for your suggestions!

for e in ./*.cutoff.txt; do
if grep -q -E 'COX1|Cu-oxidase' $e
then
    echo xyz >$e.match.txt
else
    echo
fi

if grep -q -E 'AMO' $e
then
    echo abc >$e.match.txt
else
    echo
fi; done

Any comments on that? It seems inefficient to grep twice, but it works...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rororo
  • 815
  • 16
  • 31