LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   unary operator expected (https://www.linuxquestions.org/questions/programming-9/unary-operator-expected-882781/)

hd_pulse 05-26-2011 02:27 AM

unary operator expected
 
I am trying to find the output of the following code :

Code:

i=4 j=-1  k=0
[ $i -o $j -o $k ]
echo $?
[ $i -a $j -a $k ]
echo $?
[ $i -a $j -o $k ]
echo $?

But it generates an error as :
./10aj: line 4: [: -1: unary operator expected
2
./10aj: line 6: [: -1: unary operator expected
2
./10aj: line 8: [: -1: unary operator expected
2

where am I making mistake??

what unary operator is 'shell' talking about?

neonsignal 05-26-2011 06:41 AM

The script expression has a boolean operator. After the operator, it expects to see a boolean expression. A boolean expression could be either a boolean value, or a more complex expression beginning with a unary operator. Since '-1' cannot be evaluated as a boolean value, it looks for a unary operator. When that fails, it gives the appropriate syntax error.

I'm not sure what you are trying to do. Perhaps you are trying to check if the value is non-zero?

grail 05-26-2011 06:53 AM

Maybe you were trying to do:
Code:

#!/bin/bash

i=4
j=-1
k=0

(( i || j || k )) && echo line 1 $?
(( i && j && k )) && echo line 2 $?
(( i && j || k )) && echo line 3 $?

I added line number so we could tell what was true

MTK358 05-26-2011 07:54 AM

Also, doesn't the dash before the number make test think that it's an option? Or can it somehow figure out that it means "negative"?

crts 05-26-2011 07:58 AM

Quote:

Originally Posted by neonsignal (Post 4367539)
The script expression has a boolean operator. After the operator, it expects to see a boolean expression. A boolean expression could be either a boolean value, or a more complex expression beginning with a unary operator. Since '-1' cannot be evaluated as a boolean value, it looks for a unary operator. When that fails, it gives the appropriate syntax error.

I'm not sure what you are trying to do. Perhaps you are trying to check if the value is non-zero?

Hi,

I got used to evaluating expressions like this
Code:

[[ $i || $j || $k ]]
which works fine. However, this thread got me curious and I experimented with the expressions in the OP. So either I am missing something very fundamental about the '-o' and '-a' operators or there is another inconsistency in bash (or in the implementation of '[').
Code:

[ 4 -a -1 -a 0 ] # this returns an error
[ 4 -a -10 -a 0 ] #this, however, is OK

So my first thought was, the '[' implicitly assumes the '-n' if it sees something like
Code:

[ 4 ] # implicitly assumes [ -n 4 ]
So an expression like
Code:

[ 4 -a 0 -a 3]
will implicitly be handled like
Code:

[ -n 4 -a -n 0 -a -n 3]
In case you do not want to test for a nonzero string by default you should be able to pass another unary operator like -z. Now '[' needs to determine if the token after '-a' is an operator which overrides the default. The decision if the next token is an operator is based upon if the first character is a minus '-' and if the token has length 2. If one of those conditions is not met then '[' determines that the token is not an operator. Observe:
Code:

$ [ 4 -a -1 -a 0 ] && echo true || echo false
bash: [: -1: unary operator expected
false
$ [ 4 -a -10 -a 0 ] && echo true || echo false
true
$ [ 4 -a -n -1 -a 0 ] && echo true || false
true
$ [ 4 -a -z -1 -a 0 ] && echo true || echo false
false

I consider not evaluating the second character of the second token to determine if it is an operator as inconsistency. Maybe even a bug?
Well, that is my theory so far. But it gets even stranger:
Code:

$ [ -1 ] && echo true || echo false
true
$ [ 0 -a -1 ] && echo true || echo false
true

Why is it handling it correctly when there is only one '-a' operator involved? My guess would be, that the whole expression is parsed and rearranged to account for operator precedence. Something must go wrong while it is parsed.

I had a quick look at the manpage but did not find anything that explains this behavior. Did I overlook something?

Other opinions/explanations of the observed behavior are welcome.

grail 05-26-2011 11:05 AM

Well interestingly if you use [[]] you get perhaps more informative error messages:
Code:

./d.sh: line 3: conditional binary operator expected
./d.sh: line 3: syntax error near `-a'
./d.sh: line 3: `[[ 4 -a -1 -a 0 ]] && echo true'

It appears that test, [ or [[ perceives any single character after a dash as an option and only once the single number is adjoined to another number
does it then become a negative. I am not sure I would call it a bug as perhaps how you would program around this??

crts 05-26-2011 01:21 PM

Quote:

Originally Posted by grail (Post 4367769)
Well interestingly if you use [[]] you get perhaps more informative error messages:

Yes, I tried that. I am not sure if the parsing mechanism is the same as with '['. But the behavior of '[[' differs:
Code:

$ [[ 1 -a 3 -a 4 ]] && echo true || echo false
bash: conditional binary operator expected
bash: syntax error near `-a'
$ [ 1 -a 3 -a 4 ] && echo true || echo false
true

So I'd like to focus on '[' before tackling '[['.

I found the section in bash man-page that describes the intended behavior of '['"
Code:

      test expr
      [ expr ]
              Return  a  status of 0 or 1 depending on the evaluation of the conditional expres‐
              sion expr.  Each operator and operand must be a  separate  argument.  Expressions
              are composed of the primaries described above under CONDITIONAL EXPRESSIONS.  test
              does not accept any options, nor does it accept and ignore an argument  of  --  as
              signifying the end of options.

              Expressions  may  be  combined using the following operators, listed in decreasing
              order of precedence.  The evaluation depends  on  the  number  of  arguments;  see
              below.
              ! expr True if expr is false.
              ( expr )
                    Returns  the value of expr.  This may be used to override the normal prece‐
                    dence of operators.
              expr1 -a expr2
                    True if both expr1 and expr2 are true.
              expr1 -o expr2
                    True if either expr1 or expr2 is true.

              test and [ evaluate conditional expressions using a set of rules based on the num‐
              ber 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 sec‐
                    ond argument is null.  If the first argument is one  of  the  unary  condi‐
                    tional 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
                    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-argu‐
                    ment 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.

Now let's have a look at the intended behavior when there are three arguments:
Code:

$ [ 1 -a -1 ] && echo true || echo false
true

'[' sees three arguments and that the second argument is the binary operator '-a'. According to the man-page it determines that the first argument and the second argument must be operands and thus treated as such.

To further verify that the second argument is decisive for its behavior when there are three arguments passed, consider the following:
Code:

# According to the man-page '-a' can also be a unary operator:
#      -a file
#              True if file exists.
$ ls
file
$ [ ! -a file ] && echo true || echo false
true

The fact that there are three arguments determines that '-a' has to be the binary operator. Hence, '!' is not treated as the negation operator but simply as string. So we can not use this construct to test if a file is present and then negate the outcome. To do this we have to change the order in which the expression is being evaluated. This can be done with braces:
Code:

$ ls
file
$ [ ! \( -a file \) ] && echo true || echo false
false

According to the five-arguments-rule the braces cause to evaluate the expression inside them first which results in '-a' being interpreted according to the two-arguments-rule as unary operator.
More fun with unary operators:
Code:

$ ls
file
$ [ 1 -a -a file ] && echo true || echo false
true
$ [ 1 -a -a nofile ] && echo true || echo false
false

Unary operator '-a' takes precedence over binary '-a' if there are more than three arguments. According to the four-argument-rule the above first evaluates '-a file' according to the two-rule-argument and the result according to the three-rule-argument.

According to the five-or-more-rule the following should have worked:
Code:

$ [ \( 1 -a -1 \) -a 4 ] && echo true
bash: [: -1: unary operator expected
$ [ 1 -a \( -1 -a 4 \) ] && echo true
bash: [: -1: unary operator expected

At least with braces the expr. within should have been evaluated according to the three-argument-rule. Therefore it should have determined that '-a' is a binary operator and treat '-1' as operand. Enclosed within braces, there should be no confusion about misinterpreting '-1' as unary operator.
Apparently, this is not the case.

After all, I am more and more inclined to consider this behavior as a bug. Or is my logic failing me?

neonsignal 05-26-2011 07:43 PM

I don't quite see the problem. The original post is using '-a' operators with numbers, but they are intended to be used with conditional values. I'll agree that the error message is not very informative, but the only surprising thing is how lenient the shell is.
Code:

[ 0 -a 0 ] && echo true || echo false
true
[ -0 -a -0 ] && echo true || echo false
true
[ "" -a "" ] && echo true || echo false
false

If the intention is to check the values, then comparisons can be used, and it all works (or better still, use double parentheses as grail suggests).
Code:

[ 4 -ne 0 -o -1 -ne 0 -o 0 -ne 0 ] && echo true || echo false
true
[ 4 -ne 0 -a -1 -ne 0 -a 0 -ne 0 ] && echo true || echo false
false


wje_lq 05-26-2011 11:01 PM

Code:

./10a line 4: [: -1: unary operator expected
Nobody expects the unary operator.

That is all.


All times are GMT -5. The time now is 02:32 AM.