[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Home made backup system



On Thu, Dec 19, 2019 at 09:47:03AM +0100, tomas@tuxteam.de wrote:
> So this "if" means:
> 
>   if           ## if
>   test         ##
>   -z "$home"   ## the value of $home is empty
>   -o           ## or
>   \!           ## there is NOT
>   -d "$home"   ## a directory named "$home"
>                ## we're homeless.

Expanding on what I said in a previous message, the reason this is not
portable is because parsing this kind of expression is hard, and shells
did not all agree on how to do it.

So rather than try to enforce some kind of difficult parsing within
test, POSIX decided to scrap the whole thing.  In POSIX's wording:

  The XSI extensions specifying the -a and -o binary primaries and the
  '(' and ')' operators have been marked obsolescent. (Many expressions
  using them are ambiguously defined by the grammar depending on the
  specific expressions being evaluated.) Scripts using these expressions
  should be converted to the forms given below.

Shells that don't support binary -o and -a are compliant by default, and
shells that DO support it are simply offering an extension.  BUT, this is
only true for some expressions involving -o and -a.  Not all expressions.

What POSIX actually settled on for the test command is a strict
interpretation based on the number of arguments passed.

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html

  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.

    [OB XSI]  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.

    [OB XSI]  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.


So... your binary -o and -a are only allowed as extensions in one of the
"results are unspecified" cases, e.g. when there are 5 or more arguments
given to test.  Your code above has 6 arguments, so this is allowable, if
a given shell chooses to attempt it.  Bash is one of the shells that does.

Still, you shouldn't be writing this type of code.  If you're going
to require bash extensions, just go all in and use [[ -z $v || ! -d $v ]]
instead.  Otherwise, string together two test commands.

(Also remember that test -a is a legacy synonym for test -e, so a shell
that wants to parse binary -a first has to figure out whether it's
looking at a unary -a or a binary -a.  Bash's [[ || ]] doesn't have
that problem.)

The POSIX page actually goes into a lot more detail about some of the
historical glitches with test.  It's worth a read.

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html#tag_20_128_16


Reply to: