6

I've read man test and found:

NOTE: Binary -a and -o are inherently ambiguous. Use 'test EXPR1 && test EXPR2' or 'test EXPR1 || test EXPR2' instead.

Can someone explain what ambiguously in -a and -o?

English is not my native language. As I understood, Kevin Braunsdorf and Matthew Bradburn (authors of this man page) advice us to use && instead of -a just because -a could be used as a key in another binaries, because -a could have different possible meanings.

So, -a is not associated with logical AND when we use test. It's not clear for someone, so people thinking like: "Hmm, what is that key? I don't know, not sure it is logical AND, I must visit man..."

The only reason I could find is that code with && is easier to read for people than code with -a (just an example):

With -a:

if [ -f "${my_var_with_filename}" -a -n "${my_another_array_var_with_filename[2]}" -o -r /just/path/to/file ]
then
...
fi

Same with &&:

if [ -f "${my_var_with_filename}" ] && [ -n "${my_another_array_var_with_filename[2]}" ] || [ -r /just/path/to/file ]
then
...
fi

As for me, both examples are syntactically complex, and it's hard to read bash code with multiple conditions, variables, array variables and logical operators.

So, my questions are:

  1. Do you agree with that -a and -o in test are "inherently ambiguous"?

  2. In multiple conditions, do you use -a(-o) or &&(||)?

  3. Someone has arguments to unequivocally name -a and -o "ambiguous"? Or it's just authors' opinion? Should they advice us to use && instead of -a based only on their opinion in man page?

Viktor Khilin
  • 1,760
  • 9
  • 21
  • an other less known difference is the side effects: `test EXPR1 -a EXPR2` will always evaluate both EXPR1 and EXPR2, while `test EXPR1 && test EXPR2` will, like in C language, evaluate EXPR2 only if EXPR1 was true. When EXPR1 and EXPR2 include results from other commands, side effects can matter – A.B Jan 31 '18 at 23:36

3 Answers3

5

Let's stick with strict POSIX, with the extension that -a represents logical AND. Now, consider the command

test "$1" -a "$2"

You might think this will test that both $1 and $2 are non-empty strings. But you would be wrong. If $1 == "!", then you have the command

test ! -a "$2"

which fails because -a is not a supported unary operator.

(This is adapted from the example given in the POSIX specification itself.)


If this is actually bash, it gets worse. -a is a valid unary operator, so now you are testing if the file named by $2 does not exist.

chepner
  • 497,756
  • 71
  • 530
  • 681
2

According to POSIX, [ -f file1 -a -f file2 ] is unspecified behavior. Let alone much longer -a/-o constructs.

SYNOPSIS:

  test [expression]
  [ expression ]

OPERANDS:

... ...

In the following list, $1, $2, $3, and $4 represent the arguments
presented to test:

0 arguments:

    Exit false (1).

1 argument:

    Exit true (0) if $1 is not null; otherwise, exit false.

2 arguments:

      - If $1 is '!', exit true if $2 is null, false if $2 is not null.

      - If $1 is a unary primary, exit true if the unary test is true,
        false if the unary test is false.

      - Otherwise, produce unspecified results.

 

3 arguments:

      - If $2 is a binary primary, perform the binary test of $1 and $3.

      - If $1 is '!', negate the two-argument test of $2 and $3.

      - If $1 is '(' and $3 is ')', perform the unary test of $2. On
        systems that do not support the XSI option, the results are
        unspecified if $1 is '(' and $3 is ')'.

      - Otherwise, produce unspecified results.

4 arguments:

      - If $1 is '!', negate the three-argument test of $2, $3, and $4.

      - If $1 is '(' and $4 is ')', perform the two-argument test of $2
        and $3.On systems that do not support the XSI option, the results
        are unspecified if $1 is '(' and $4 is ')'.

      - Otherwise, the results are unspecified.   

>4 arguments:

    The results are unspecified. 
pynexj
  • 19,215
  • 5
  • 38
  • 56
-2
  1. While the test binary has the -a and -o flags, it is not common. Even within the test command itself, -a is ambiguous since it can be used to both check if a file exists or represent the binary AND operator.

  2. Use && and || for binary AND and binary OR. They are more common and formalized as a standard with the C language.

  3. See my response to your first question. Even within the test command itself, -a can be ambiguous; use && and || instead.

Nishnha
  • 323
  • 2
  • 4
  • Referring to the C language standard isn't really useful, since shell `&&` and `||` have *equal* precedence, in contrast to the C versions of the operators. – chepner Jan 31 '18 at 17:35