[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] confusing/obsolete handling of test -t operator (and doc warning
From: |
Stephane Chazelas |
Subject: |
[PATCH] confusing/obsolete handling of test -t operator (and doc warnings against using -o/-a) |
Date: |
Thu, 6 Jul 2023 07:29:15 +0100 |
Hello,
test -t X
Always returns false and doesn't report an error about that
invalid number (beside the point here, but in ksh/zsh, that X is treated
as an arithmetic expression and evaluates to 0 if $X is not set).
While:
test -t X -a Y
returns a "too many arguments" error.
test X -a -t -a Y
returns false (without error and regardless of whether any fd is a tty)
while
test X -a Y -a -t
returns true
While for other unary operators that gives:
$ bash -c 'test X -a -x -a Y'
bash: line 1: test: too many arguments
No big deal as in all those the behaviour is unspecfied by
POSIX (non-numeric argument to -t, or more than 4 arguments, -a
deprecated).
It seems to be explained by what looks like a remnant from the
time where [ -t ] was short for [ -t 1 ] in the code of
unary_operator() in test.c
> /* the only tricky case is `-t', which may or may not take an argument. */
Not anymore.
> if (op[1] == 't')
> {
> advance (0);
> if (pos < argc)
> {
> if (legal_number (argv[pos], &r))
> {
> advance (0);
> return (unary_test (op, argv[pos - 1], 0));
> }
> else
> return (FALSE);
Maybe the intention was to do a isatty(1) here instead of always
returning false, but that and the fact that advance() is not called only
confuses things.
> }
> else
> return (unary_test (op, "1", 0));
That part is never reached AFAICT as unary_operator() is never
called with pos == argc.
I beleive that whole code can go as -t is now always a unary
operator, and it would be more useful to report an error when
the operand is not a number.
I also noticed that the fact that -a/-o were deprecated (by POSIX at
least) and made for unreliable test expressions was not noted in the
manual. So I suggest the patch below:
diff --git a/doc/bashref.texi b/doc/bashref.texi
index 85e729d5..00fbab69 100644
--- a/doc/bashref.texi
+++ b/doc/bashref.texi
@@ -4215,14 +4215,14 @@ Operator precedence is used when there are five or more
arguments.
@item ! @var{expr}
True if @var{expr} is false.
-@item ( @var{expr} )
+@item ( @var{expr} ) (DEPRECATED)
Returns the value of @var{expr}.
This may be used to override the normal precedence of operators.
-@item @var{expr1} -a @var{expr2}
+@item @var{expr1} -a @var{expr2} (DEPRECATED)
True if both @var{expr1} and @var{expr2} are true.
-@item @var{expr1} -o @var{expr2}
+@item @var{expr1} -o @var{expr2} (DEPRECATED)
True if either @var{expr1} or @var{expr2} is true.
@end table
@@ -4283,11 +4283,26 @@ Otherwise, the expression is parsed and evaluated
according to
precedence using the rules listed above.
@end enumerate
-@item 5 or more arguments
+@item 5 or more arguments (DEPRECATED)
The expression is parsed and evaluated according to precedence
using the rules listed above.
@end table
+In the 4 or 5 arguments case, the use of @samp{(}, @samp{)}, binary
+@samp{-a}, binary @samp{-o} make for unreliable test expressions. For
+instance @code{test "$x" -a ! "$y"} becomes a test for whether a
+@samp{!} file exists if @code{$x} is @samp{(} and @code{$y} is
+@samp{)} and @code{[ -f "$file" -a ! -L "$file" ]} fails with a
+syntax error for a file called @samp{==}. Which explains why those
+are deprecated as they have been in the POSIX specification of the
+@code{test} utility since 2008.
+
+Each invocation of @code{[} / @code{test} should perform a single test
+and several invocations may be chained with the @code{&&} or @code{||}
+shell operators to achieve the same result as the @code{-a} and
+@code{-o} operators reliably as in @code{test "$x" && test ! "$y"} or
+@code{[ -f "$file" ] && [ ! -L "$file" ]} in the examples above.
+
When used with @code{test} or @samp{[}, the @samp{<} and @samp{>}
operators sort lexicographically using ASCII ordering.
diff --git a/test.c b/test.c
index 2b12197a..e16337a5 100644
--- a/test.c
+++ b/test.c
@@ -476,24 +476,6 @@ unary_operator (void)
if (test_unop (op) == 0)
return (FALSE);
- /* the only tricky case is `-t', which may or may not take an argument. */
- if (op[1] == 't')
- {
- advance (0);
- if (pos < argc)
- {
- if (legal_number (argv[pos], &r))
- {
- advance (0);
- return (unary_test (op, argv[pos - 1], 0));
- }
- else
- return (FALSE);
- }
- else
- return (unary_test (op, "1", 0));
- }
-
/* All of the unary operators take an argument, so we first call
unary_advance (), which checks to make sure that there is an
argument, and then advances pos right past it. This means that
@@ -603,7 +585,7 @@ unary_test (char *op, char *arg, int flags)
case 't': /* File fd is a terminal? */
if (legal_number (arg, &r) == 0)
- return (FALSE);
+ integer_expected_error (arg);
return ((r == (int)r) && isatty ((int)r));
case 'n': /* True if arg has some length. */
- [PATCH] confusing/obsolete handling of test -t operator (and doc warnings against using -o/-a),
Stephane Chazelas <=