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. Even though many implementations will continue to support
these obsolescent forms, scripts should be extremely careful when
dealing with user-supplied input that could be confused with
these and other primaries and operators. Unless the application
developer knows all the cases that produce input to the script,
invocations like:
test "$1" -a "$2"
should be written as:
test "$1" && test "$2"
to avoid problems if a user supplied values such as $1 set to '!'
and $2 set to the null string. That is, in cases where maximal
portability is of concern, replace:
test expr1 -a expr2
with:
test expr1 && test expr2
and replace:
test expr1 -o expr2
with:
test expr1 || test expr2
but note that, in test, -a
has higher precedence than -o
while
"&&"
and "||"
have equal precedence in the shell.
Parentheses or braces can be used in the shell command language
to effect grouping.
Parentheses must be escaped when using sh; for example:
test \( expr1 -a expr2 \) -o expr3
This command is not always portable even on XSI-conformant
systems depending on the expressions specified by expr1, expr2,
and expr3. The following form can be used instead:
( test expr1 && test expr2 ) || test expr3
The two commands:
test "$1"
test ! "$1"
could not be used reliably on some historical systems. Unexpected
results would occur if such a string expression were used and $1
expanded to '!'
, '('
, or a known unary primary. Better constructs
are:
test -n "$1"
test -z "$1"
respectively.
Historical systems have also been unreliable given the common
construct:
test "$response" = "expected string"
One of the following is a more reliable form:
test "X$response" = "Xexpected string"
test "expected string" = "$response"
Note that the second form assumes that expected string could not
be confused with any unary primary. If expected string starts
with '-'
, '('
, '!'
, or even '='
, the first form should be used
instead. Using the preceding rules without the XSI marked
extensions, any of the three comparison forms is reliable, given
any input. (However, note that the strings are quoted in all
cases.)
Because the string comparison binary primaries, '='
and "!="
,
have a higher precedence than any unary primary in the greater
than 4 argument case, unexpected results can occur if arguments
are not properly prepared. For example, in:
test -d $1 -o -d $2
If $1 evaluates to a possible directory name of '='
, the first
three arguments are considered a string comparison, which shall
cause a syntax error when the second -d
is encountered. One of
the following forms prevents this; the second is preferred:
test \( -d "$1" \) -o \( -d "$2" \)
test -d "$1" || test -d "$2"
Also in the greater than 4 argument case:
test "$1" = "bat" -a "$2" = "ball"
syntax errors occur if $1 evaluates to '('
or '!'
. One of the
following forms prevents this; the third is preferred:
test "X$1" = "Xbat" -a "X$2" = "Xball"
test "$1" = "bat" && test "$2" = "ball"
test "X$1" = "Xbat" && test "X$2" = "Xball"
Note that none of the following examples are permitted by the
syntax described:
[-f file]
[-f file ]
[ -f file]
[ -f file
test -f file ]
In the first two cases, if a utility named [‐f exists, that
utility would be invoked, and not test. In the remaining cases,
the brackets are mismatched, and the behavior is unspecified.
However:
test ! ]
does have a defined meaning, and must exit with status 1.
Similarly:
test ]
must exit with status 0.