# sh -> vars in [ -a ] aren't evalueated correctly



## Seeker (Nov 10, 2011)

```
# Both vars are undefined
[ ! "$DESTDIR" ] && echo "TRUE that '\$DESTDIR' doesn't exist!"
[ "$changing_kern" ] || echo "TRUE that '\$changing_kern' doesn't exist!"

echo
# NONE if statement, should echo anything !!!
    # + [ ! '' -a '' ]
    if [ ! "$DESTDIR"  -a  "$changing_kern" ]; then
        echo "If statement returned TRUE, with '-a'"
    fi

    # + [ ! '' ]
    # + [ '' ]
    if [ ! "$DESTDIR" ] && [ "$changing_kern" ]; then
        echo "If statement returned TRUE, with '&&'"
    fi
```


----------



## wblock@ (Nov 10, 2011)

"Doesn't exist" is misleading, "not defined" or "not set" would be better.

Instead of just ! "$string", I would use -n to test if a string is non-zero in length, or -z to see if it's zero in length.  See test(1).  Also, the last if should use -a instead of &&.


----------



## Seeker (Nov 10, 2011)

wblock@ said:
			
		

> "Doesn't exist" is misleading, "not defined" or "not set" would be better.


True.
But this is _advanced_, so I've thought it is implied, for those that look at the code, not what is being echoed.



			
				wblock@ said:
			
		

> Instead of just ! "$string", I would use -n to test if a string is non-zero in length, or -z to see if it's zero in length.  See test(1).  Also, the last if should use -a instead of &&.



Point is, that I like to use shorter sintax in order to achieve same goal, which means *without* -z and -n
The last if. is exactly *one that works* for *short form!*

So the question is ..., WHY does it fail in first form?!


----------



## wblock@ (Nov 10, 2011)

According to test(1),
`% [ ! \( "$DESTDIR" \) ] && echo "DESTDIR is undefined"`
and that works.

This is better, IMO, being more specific about what is being tested.  And it's shorter.
`% [ -z "$DESTDIR" ] && echo "DESTDIR is undefined"`


----------



## Seeker (Nov 10, 2011)

And this one is shortest and *works*:
`# [ ! "$DESTDIR" ] && echo "DESTDIR is undefined"`

So why doesn't 2 shorts work inside of a test []


----------



## wblock@ (Nov 10, 2011)

And this one does not work:
`% [ ! "$DESTDIR" -a "$changing_kern" ] && echo "DESTDIR is not defined, changing_kern is defined"`

There's a difference between what test(1) evaluates for an expression and the exit status it returns.


----------



## Seeker (Nov 10, 2011)

Both are *undefined* and I still get output:

```
DESTDIR is not defined, changing_kern is defined
```
Why did it returned *true* exit code here, if left side of expression was *true* and left one *false*?


----------



## wblock@ (Nov 10, 2011)

Seeker said:
			
		

> Both are *undefined* and I still get output:
> 
> ```
> DESTDIR is not defined, changing_kern is defined
> ...



Exactly, it does not work.  Why?  Could be one of the ambiguous cases described in test(1).  It works if you rewrite it to not use -a:
`% [ ! "$DESTDIR" ] && [ "$changing_kern" ] && echo "DESTDIR is not defined, changing_kern is defined"`
Or if it is rewritten to use grouping parens:
`% [ \( ! "$DESTDIR" \) -a "$changing_kern" ] && echo "DESTDIR is not defined, changing_kern is defined"`

This works, and indicates what is being tested:
`% [ -z "$DESTDIR" -a -n "$changing_kern" ] && echo "DESTDIR is not defined, changing_kern is defined"`


----------



## mix_room (Nov 11, 2011)

Seeker said:
			
		

> And this one is shortest and *works*:
> `# [ ! "$DESTDIR" ] && echo "DESTDIR is undefined"`
> 
> So why doesn't 2 shorts work inside of a test []



They don't necessarily do the same thing though. 

If "$DESTDIR" evaluates to false, then !(FALSE) == TRUE. 
You should really test for the closest possible thing. Adding a single character is not making your code substantially longer, but much clearer in its meaning.


----------



## Seeker (Nov 11, 2011)

wblock@ said:
			
		

> Exactly, it does not work.  Why?  Could be one of the ambiguous cases described in test(1).  It works if you rewrite it to not use -a:
> `% [ ! "$DESTDIR" ] && [ "$changing_kern" ] && echo "DESTDIR is not defined, changing_kern is defined"`
> Or if it is rewritten to use grouping parens:
> `% [ \( ! "$DESTDIR" \) -a "$changing_kern" ] && echo "DESTDIR is not defined, changing_kern is defined"`
> ...


Well, as a goal to achieve shortest syntax, *grouping parentheses* are totally out of game.
If -a and -o are ambiguous and there is no panacea, then I'll replace them with:
*... ] && [ ...* and *... ] || [ ...* respectively



			
				mix_room said:
			
		

> They don't necessarily do the same thing though.
> 
> If "$DESTDIR" evaluates to false, then !(FALSE) == TRUE.
> You should really test for the closest possible thing. Adding a single character is not making your code substantially longer, but much clearer in its meaning.


I don't understand, what you wanted to say:
Making DESTDIR var *false, FALSE* or *False*, will never echo anything, for:

```
[ ! "$DESTDIR" ] && echo "DESTDIR is undefined"
```


----------



## jilles@ (Nov 13, 2011)

I recommend not using -a, -o and parentheses at all, but the construct

```
[ ! "$string1" -a "$string2" ]
```
is particularly nasty. Some test implementations such as the builtins in bash and FreeBSD sh consider -a and -o "binary primaries" (POSIX SUSv4 XCU 4 Utilities test) and obey the rule for 4 arguments that if the first is '!' the result is the negation of the 3-argument test on the remaining arguments, determined by the binary primary -a.

This really does not make much sense (!, -a and -o are really more like "operators" than "primaries") but I think it is what the standard says.

In any case, if you use -a and -o, you will find more nasty surprises because some expressions containing them are ambiguous, so I suggest not using them.


----------



## Seeker (Nov 13, 2011)

Yes. And here is another argument for use of:
*... ] && [ ...* and *... ] || [ ...*

test(1)


> BUGS
> Both sides are always evaluated in -a and -o.  For instance, the writable
> status of file will be tested by the following command even though the
> former expression indicated false, which results in a gratuitous access
> ...


----------

