Hi!
Yes, the argument after -n is literal. As-expected so far.
Notably, -a, -o, (, and ) are all killed in POSIX Issue 8 (Draft 2.1),
via Austin Group Defect 1330:
https://www.austingroupbugs.net/view.php?id=1330
They were marked obsolescent and optional in Issue 7 and earlier
(because they're awful and underspecified, you should never use them).
There are a few ways of parsing this:
if you're using a full-blown per-spec precedence-obeying parser,
like voreutils test or ksh, then you'll parse it like
( -n '-ne' ) && ( -n ':0' )
which resolves to true:
$ ~/code/voreutils/out/cmd/test -n -ne -a -n :0; echo $?
0
$ ksh -c 'test -n -ne -a -n :0; echo $?'
0
(well, idk if ksh uses a parse tree, but it produces the same result
that voreutils test does, and that does use one).
If you parse it in compatibility mode, like dash, bash,
and GNU coreutils test, then you'll parse it like
( '-n' -ne '-a' ) {garbage}
which is illegal:
$ bash -c 'test -n -ne -a -n :0'
bash: line 1: test: -n: integer expression expected
$ test -n -ne -a -n :0
./dash: 1: test: Illegal number: -n
$ /bin/test -n -ne -a -n :0
/bin/test: invalid integer ‘-n’
(if you make the -ne arguments integers the garbage -n :0 makes it
illegal anyway, and all of the above agree).
This is a much more interesting case: the obvious parse here is, indeed,
( -n '-ne' ) && ( -n ':0' )
and GNU coreutils test, voreutils test, and ksh are in agreement now:
$ /bin/test '(' -n -ne ')' -a '(' -n :0 ')'; echo $?
0
$ ~/code/voreutils/out/cmd/test '(' -n -ne ')' -a '(' -n :0 ')'; echo
$?
0
$ ksh -c "test '(' -n -ne ')' -a '(' -n :0 ')'; echo \$?"
0
Nevertheless, bash and dash still parse this differently:
$ test '(' -n -ne ')' -a '(' -n :0 ')'; echo $?
./dash: 9: test: Illegal number: -n
2
$ bash -c "test '(' -n -ne ')' -a '(' -n :0 ')'; echo \$?"
bash: line 1: test: -n: integer expression expected
2
by substituting the first -n for 0, then the first ) for 0,
then adding a closing paren, yielding:
$ test '(' 0 -ne ')' -a '(' -n :0 ')'; echo $?
./dash: 16: test: Illegal number: )
2
$ bash -c "test '(' 0 -ne ')' -a '(' -n :0 ')'; echo \$?"
bash: line 1: test: ): integer expression expected
2
$ test '(' 0 -ne 0 -a '(' -n :0 ')'; echo $?
./dash: 18: test: closing paren expected
2
$ bash -c "test '(' 0 -ne 0 -a '(' -n :0 ')'; echo \$?"
bash: line 1: test: `)' expected
2
$ test '(' 0 -ne 0 -a '(' -n :0 ')' ')'; echo $?
1
$ bash -c "test '(' 0 -ne 0 -a '(' -n :0 ')' ')'; echo \$?"
1
we can see that they parse it as
( ( '-n' -ne ')' ) && ( -n ':0' )
which is unbalanced and puts -n and ) in integer spots.
Similarly, this is test ( -n -nt ) -a ( -n :0 ), which, again,
would make most sense to be
( -n '-nt' ) -a ( -n ':0' )
and, similarly, the same suspects agree:
$ /bin/test '(' -n -nt ')' -a '(' -n :0 ')'; echo $?
0
$ ~/code/voreutils/out/cmd/test '(' -n -nt ')' -a '(' -n :0 ')'; echo
$?
0
$ ksh -c "test '(' -n -nt ')' -a '(' -n :0 ')'; echo \$?"
0
and disagree:
$ test '(' -n -nt ')' -a '(' -n :0 ')'; echo $?
./dash: 26: test: closing paren expected
2
$ bash -c "test '(' -n -nt ')' -a '(' -n :0 ')'; echo \$?"
bash: line 1: test: `)' expected
2
and through the same reduction process:
$ test '(' -n -nt ')' -a '(' -n :0 ')' ')'; echo $?
1
$ bash -c "test '(' -n -nt ')' -a '(' -n :0 ')' ')'; echo \$?"
1
we arrive at the same parse:
( ( '-n' -nt ')' ) && ( -n ':0' )
which is similarly unbalanced,
and strace confirms they both stat 0 and ).
The correct solution is
if [ -n "$variable" ] && [ cond ] && [ cond ]
then
which is, since Issue 8, the only solution, and before Issue 7,
the only one that wasn't a damned underspecified mine-field.
IOW Just Don't Put Multiple Conditions In Test(1);
quoth Issue 7, XCU, test, APPLICATION USAGE:
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
Best,
наб