Issue
I have not used this particular construct for the bash builtin test
/ [
commands, but I ran into it today and I'm confused. It looks like this:
[ -n "${FOO}" -a -r ${FOO}/bar ] && echo OK
I know what each switch does individually, but I am not sure about the behavior when they are grouped like this. Specifically, the -a -r [operand]
part. Here is what the man
page has to say:
test and [ evaluate conditional expressions using a set of rules based on the number of arguments.
0 arguments
The expression is false.
1 argument
The expression is true if and only if the argument is not null.
2 arguments
If the first argument is !, the expression is true if and only if the second argument is null. If the first argument is one of the unary conditional operators listed above under CONDITIONAL EXPRESSIONS, the expression is true if the unary test is true. If the first argument is not a valid unary conditional operator, the expression is false.
3 arguments
The following conditions are applied in the order listed. If the second argument is one of the binary conditional operators listed above under CONDITIONAL EXPRESSIONS, the result of the expression is the result of the binary test using the first and third arguments as operands. The -a and -o operators are considered binary operators when there are three arguments. If the first argument is !, the value is the negation of the two-argument test using the second and third arguments. If the first argument is exactly ( and the third argument is exactly ), the result is the one-argument test of the second argument. Otherwise, the expression is false.
4 arguments
If the first argument is !, the result is the negation of the three-argument expression composed of the remaining arguments. Otherwise, the expression is parsed and evaluated according to precedence using the rules listed above.
5 or more arguments
The expression is parsed and evaluated according to precedence using the rules listed above.
Ok, great. Since I provide 5 arguments, the expression will be parsed and the rules applied. I assume it will be split and evaluated in 2 parts, like this:
[ -n "${FOO}" ] && [ -a -r ${FOO}/bar ] && echo OK
But that isn't the case, as this yields [: -r: binary operator expected
, it doesn't like [ -a -r ... ]
. Of course, this works:
[ -n "${FOO}" ] && [ -a ${FOO}/bar ] && [ -r ${FOO}/bar ] && echo OK
And so does this:
[ -n "${FOO}" -a ${FOO}/bar ] && echo OK
but this fails:
[ -n "${FOO}" -r ${FOO}/bar ] && echo OK
adding to my confusion is the part that says:
The -a and -o operators are considered binary operators when there are three arguments
How exactly is the -a
handled as a binary operator? I believe it tests the existence of a file, but does it have some other behavior here? What does it do with the second operand? How does the Bash builtin test
parse the arguments in the original example? What am I missing?
Solution
In your command
test -n "${FOO}" -r ${FOO}/bar
you have two conditions, but you don't tell test whether both need to be true, or if it is sufficient that one is true, which is why you get an error. Hence you have to write one of
test -n "${FOO}" -a -r ${FOO}/bar
test -n "${FOO}" -o -r ${FOO}/bar
In your command
-n "${FOO}" -a ${FOO}/bar
you also have two conditions, but here you clearly say (using -a
) that both need to be true. Hence this is OK.
Answered By - user1934428