mirror of
https://github.com/skeeto/w64devkit.git
synced 2026-10-24 17:39:18 +03:00
Address the review in #428: plainer wording throughout, w64devkit's changes marked where they apply and listed one per item, job names, word-deletion keys, exec, cd -L/-P, CDPATH, PATH separators, and the native Windows environment explained, HOME, TERM_PROGRAM and the other variables clarified, and a pointer to the POSIX shell language. The busybox page gains context about the kit and its launchers. Drop [[ from all three pages, since the shell no longer has it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
633 lines
12 KiB
Groff
633 lines
12 KiB
Groff
.TH TEST 1 2026-10-22 "w64devkit" "w64devkit Manual"
|
|
.SH NAME
|
|
test, [ \- evaluate a conditional expression
|
|
.SH SYNOPSIS
|
|
.B test
|
|
.I expression
|
|
.br
|
|
.B [
|
|
.I expression
|
|
.B ]
|
|
.SH DESCRIPTION
|
|
.B test
|
|
evaluates
|
|
.I expression
|
|
and reports the result in its exit status: 0 if it is true, 1 if it is
|
|
false, and 2 if it is invalid.
|
|
It reads no input and writes nothing to standard output.
|
|
.PP
|
|
.B [
|
|
is the same command with a required last argument that is a lone
|
|
.BR ] ,
|
|
which is not part of the expression.
|
|
.PP
|
|
The kit's shell has both as builtins that run this same code, so
|
|
scripts see the behavior described here; see
|
|
.BR sh (1).
|
|
Outside the shell, the
|
|
.I test.exe
|
|
launcher runs the
|
|
.B busybox
|
|
applet; see
|
|
.BR busybox (1).
|
|
There is no launcher for
|
|
.BR [ ,
|
|
which is run as
|
|
.BR "busybox [" .
|
|
Unlike bash and stock busybox-w32, the kit's shell has no
|
|
.B [[
|
|
command; see
|
|
.BR NOTES .
|
|
.PP
|
|
.B test
|
|
has no options.
|
|
Arguments such as
|
|
.B \-\-help
|
|
are ordinary words of the expression, so
|
|
.B test \-\-help
|
|
is true because its one argument is not empty.
|
|
.PP
|
|
Each operator, operand, and parenthesis is a separate argument.
|
|
In the shell, quote or escape the characters it treats specially, such
|
|
as
|
|
.BR ( ,
|
|
.BR ) ,
|
|
.BR < ,
|
|
and
|
|
.BR > .
|
|
Strings are compared as bytes.
|
|
.SS Number of arguments
|
|
An expression of up to four arguments is first resolved by counting
|
|
them, mostly as POSIX specifies.
|
|
So an operand that looks like an operator is safe:
|
|
.B [ \-f = \-f ]
|
|
compares two strings, and
|
|
.B [ ! = ! ]
|
|
is true.
|
|
.TP
|
|
.B 0
|
|
False.
|
|
.TP
|
|
.B 1
|
|
True if the argument is not empty, whatever it is, so
|
|
.B [ \-f ]
|
|
and
|
|
.B [ ! ]
|
|
are true.
|
|
.TP
|
|
.B 2
|
|
If the first argument is
|
|
.BR ! ,
|
|
true if the second is empty.
|
|
Otherwise the first must be a unary operator and the second is its
|
|
operand, or the expression is invalid.
|
|
.TP
|
|
.B 3
|
|
If the second argument is a binary operator other than
|
|
.B \-a
|
|
or
|
|
.BR \-o ,
|
|
the result of that operator applied to the first and third, even when
|
|
the first is
|
|
.B !\&
|
|
or
|
|
.BR ( .
|
|
Otherwise, if the first is
|
|
.BR ! ,
|
|
the negation of the two-argument expression of the rest.
|
|
.TP
|
|
.B 4
|
|
If the first argument is
|
|
.BR ! ,
|
|
the negation of the three-argument expression of the rest, except that
|
|
when the third is
|
|
.B \-a
|
|
or
|
|
.BR \-o ,
|
|
the
|
|
.B !\&
|
|
applies to the second alone, where POSIX negates the whole.
|
|
.PP
|
|
Any other expression uses the full grammar under
|
|
.BR "Combining expressions" ,
|
|
apart from a quirk with parentheses described in
|
|
.BR NOTES .
|
|
.SS File tests
|
|
These take a
|
|
.I file
|
|
operand.
|
|
Every test except
|
|
.B \-L
|
|
and
|
|
.B \-h
|
|
follows symbolic links and applies to the target.
|
|
A file that cannot be examined for any reason, such as a missing path or
|
|
denied access, is treated as nonexistent, and its tests are false.
|
|
A name that ends in a directory separator can only be a directory.
|
|
.TP
|
|
.BI "\-b " file
|
|
True if
|
|
.I file
|
|
is a block special file.
|
|
Always false, since there are none.
|
|
.TP
|
|
.BI "\-c " file
|
|
True if
|
|
.I file
|
|
is a character special file.
|
|
These are the emulated devices
|
|
.IR /dev/null ,
|
|
.IR /dev/tty ,
|
|
.IR /dev/zero ,
|
|
.IR /dev/urandom ,
|
|
.IR /dev/stdin ,
|
|
.IR /dev/stdout ,
|
|
and
|
|
.IR /dev/stderr ,
|
|
and
|
|
.I /dev/fd/N
|
|
for each open file descriptor number
|
|
.IR N .
|
|
.TP
|
|
.BI "\-d " file
|
|
True if
|
|
.I file
|
|
is a directory.
|
|
.TP
|
|
.BI "\-e " file
|
|
True if
|
|
.I file
|
|
exists.
|
|
.TP
|
|
.BI "\-f " file
|
|
True if
|
|
.I file
|
|
is a regular file: it exists and is neither a directory nor one of the
|
|
emulated devices.
|
|
.TP
|
|
.BI "\-G " file
|
|
True if
|
|
.I file
|
|
exists and its group is the user's group.
|
|
busybox gives a file the same ID for its owner and its group, so this is
|
|
the same as
|
|
.BR \-O .
|
|
.TP
|
|
.BI "\-g " file
|
|
True if
|
|
.I file
|
|
has its set-group-ID bit set.
|
|
Always false.
|
|
.TP
|
|
.BI "\-k " file
|
|
True if
|
|
.I file
|
|
has its sticky bit set.
|
|
Always false.
|
|
.TP
|
|
\fB\-L\fR \fIfile\fR, \fB\-h\fR \fIfile\fR
|
|
True if
|
|
.I file
|
|
is a symbolic link, a directory junction or volume mount point, or an
|
|
app execution alias.
|
|
It does not follow the link.
|
|
.TP
|
|
.BI "\-O " file
|
|
True if
|
|
.I file
|
|
exists and is owned by the effective user.
|
|
See
|
|
.BR NOTES .
|
|
.TP
|
|
.BI "\-p " file
|
|
True if
|
|
.I file
|
|
is a named pipe.
|
|
Always false.
|
|
.TP
|
|
.BI "\-r " file
|
|
True if
|
|
.I file
|
|
exists and is readable.
|
|
See
|
|
.BR NOTES .
|
|
.TP
|
|
.BI "\-S " file
|
|
True if
|
|
.I file
|
|
is a socket.
|
|
Always false.
|
|
.TP
|
|
.BI "\-s " file
|
|
True if
|
|
.I file
|
|
exists and its size is not zero.
|
|
A directory has size zero.
|
|
.TP
|
|
.BI "\-t " fd
|
|
True if file descriptor
|
|
.I fd
|
|
is open on a console.
|
|
A pipe, a file, and the NUL device are not consoles.
|
|
The argument must be a decimal number.
|
|
.TP
|
|
.BI "\-u " file
|
|
True if
|
|
.I file
|
|
has its set-user-ID bit set.
|
|
Always false.
|
|
.TP
|
|
.BI "\-w " file
|
|
True if
|
|
.I file
|
|
exists and is writable.
|
|
See
|
|
.BR NOTES .
|
|
.TP
|
|
.BI "\-x " file
|
|
True if
|
|
.I file
|
|
exists and is executable.
|
|
See
|
|
.BR NOTES .
|
|
.SS String tests
|
|
.TP
|
|
.BI "\-n " string
|
|
True if
|
|
.I string
|
|
is not empty.
|
|
.TP
|
|
.BI "\-z " string
|
|
True if
|
|
.I string
|
|
is empty.
|
|
.TP
|
|
.I string
|
|
True if
|
|
.I string
|
|
is not empty.
|
|
This is the one-argument form, and the same as
|
|
.BR \-n .
|
|
.TP
|
|
\fIstring1\fR \fB=\fR \fIstring2\fR, \fIstring1\fR \fB==\fR \fIstring2\fR
|
|
True if the strings are equal.
|
|
.TP
|
|
\fIstring1\fR \fB!=\fR \fIstring2\fR
|
|
True if the strings are not equal.
|
|
.TP
|
|
\fIstring1\fR \fB<\fR \fIstring2\fR
|
|
True if
|
|
.I string1
|
|
sorts before
|
|
.IR string2 ,
|
|
comparing bytes.
|
|
.TP
|
|
\fIstring1\fR \fB>\fR \fIstring2\fR
|
|
True if
|
|
.I string1
|
|
sorts after
|
|
.IR string2 ,
|
|
comparing bytes.
|
|
.SS Integer comparisons
|
|
Each operand is a decimal integer, with optional leading white space, an
|
|
optional sign, and optional trailing white space.
|
|
It is a signed 64-bit value.
|
|
An operand that is not a number, such as an empty string or one in
|
|
hexadecimal, is the error
|
|
.BR "bad number" ,
|
|
and one that does not fit is the error
|
|
.BR "out of range" ;
|
|
both end the command with status 2.
|
|
.TP
|
|
\fIn1\fR \fB\-eq\fR \fIn2\fR
|
|
True if the numbers are equal.
|
|
.TP
|
|
\fIn1\fR \fB\-ne\fR \fIn2\fR
|
|
True if they are not equal.
|
|
.TP
|
|
\fIn1\fR \fB\-ge\fR \fIn2\fR
|
|
True if
|
|
.I n1
|
|
is greater than or equal to
|
|
.IR n2 .
|
|
.TP
|
|
\fIn1\fR \fB\-gt\fR \fIn2\fR
|
|
True if
|
|
.I n1
|
|
is greater than
|
|
.IR n2 .
|
|
.TP
|
|
\fIn1\fR \fB\-le\fR \fIn2\fR
|
|
True if
|
|
.I n1
|
|
is less than or equal to
|
|
.IR n2 .
|
|
.TP
|
|
\fIn1\fR \fB\-lt\fR \fIn2\fR
|
|
True if
|
|
.I n1
|
|
is less than
|
|
.IR n2 .
|
|
.SS File comparisons
|
|
.TP
|
|
\fIfile1\fR \fB\-ef\fR \fIfile2\fR
|
|
True if both files exist and are the same file: the same volume and
|
|
file index.
|
|
See
|
|
.BR NOTES .
|
|
.TP
|
|
\fIfile1\fR \fB\-nt\fR \fIfile2\fR
|
|
True if both files exist and
|
|
.I file1
|
|
was modified later than
|
|
.IR file2 ,
|
|
comparing times in whole seconds.
|
|
.TP
|
|
\fIfile1\fR \fB\-ot\fR \fIfile2\fR
|
|
True if both files exist and
|
|
.I file1
|
|
was modified earlier than
|
|
.IR file2 ,
|
|
comparing times in whole seconds.
|
|
.SS Combining expressions
|
|
From highest precedence to lowest:
|
|
.TP
|
|
\fB(\fR \fIexpression\fR \fB)\fR
|
|
True if
|
|
.I expression
|
|
is true.
|
|
Parentheses group.
|
|
.TP
|
|
\fB!\fR \fIexpression\fR
|
|
True if
|
|
.I expression
|
|
is false.
|
|
.TP
|
|
\fIexpression1\fR \fB\-a\fR \fIexpression2\fR
|
|
True if both are true.
|
|
.TP
|
|
\fIexpression1\fR \fB\-o\fR \fIexpression2\fR
|
|
True if either is true.
|
|
.PP
|
|
Both operands of
|
|
.B \-a
|
|
and
|
|
.B \-o
|
|
are always evaluated, so an error in the right one ends the command with
|
|
status 2 even when the left one decides the result.
|
|
.SH ENVIRONMENT
|
|
.TP
|
|
.B BB_GLOBBING
|
|
Unless it is
|
|
.BR 0 ,
|
|
the C runtime expands wildcards, even in quoted arguments, when
|
|
.I test.exe
|
|
starts, which serves commands typed in
|
|
.BR cmd.exe .
|
|
So
|
|
.B "test.exe \-f \(dq*.c\(dq"
|
|
typed there fails with an unknown operand error when more than one file
|
|
matches.
|
|
Set it to
|
|
.B 0
|
|
to prevent that.
|
|
.TP
|
|
.B BB_UMASK
|
|
The file mode creation mask, in octal, by default
|
|
.BR 002 .
|
|
Without its
|
|
.B 002
|
|
bit, a file owned by someone else is writable unless it is read-only;
|
|
see
|
|
.BR NOTES .
|
|
The shell's
|
|
.B umask
|
|
command sets it.
|
|
.PP
|
|
Variables that affect every busybox applet are described in
|
|
.BR busybox (1).
|
|
.SH EXIT STATUS
|
|
.TP
|
|
.B 0
|
|
The expression is true.
|
|
.TP
|
|
.B 1
|
|
The expression is false, or there are no arguments.
|
|
.TP
|
|
.B 2
|
|
The expression is invalid.
|
|
A message on standard error says why:
|
|
.BR "argument expected" ,
|
|
.BR "bad number" ,
|
|
.BR "closing paren expected" ,
|
|
.BR "missing ]" ,
|
|
.BR "out of range" ,
|
|
or
|
|
.BR "unknown operand" ,
|
|
usually after the argument at fault.
|
|
It begins with the command name, or in the shell with the shell's name,
|
|
such as
|
|
.BR sh .
|
|
.SH EXAMPLES
|
|
Run a command only if a file exists:
|
|
.PP
|
|
.RS 4
|
|
.EX
|
|
$ [ \-f config.h ] && echo found
|
|
.EE
|
|
.RE
|
|
.PP
|
|
Create a directory unless one that can be written already exists:
|
|
.PP
|
|
.RS 4
|
|
.EX
|
|
$ [ \-d build \-a \-w build ] || mkdir build
|
|
.EE
|
|
.RE
|
|
.PP
|
|
Test a variable for emptiness.
|
|
Quote it: unquoted, an empty value leaves the single argument
|
|
.BR \-n ,
|
|
which is true:
|
|
.PP
|
|
.RS 4
|
|
.EX
|
|
$ x=
|
|
$ [ \-n $x ] && echo unquoted is true
|
|
unquoted is true
|
|
$ [ \-n "$x" ] || echo quoted is false
|
|
quoted is false
|
|
.EE
|
|
.RE
|
|
.PP
|
|
Compare numbers:
|
|
.PP
|
|
.RS 4
|
|
.EX
|
|
$ [ "$n" \-ge 10 ] && echo at least ten
|
|
.EE
|
|
.RE
|
|
.PP
|
|
Rebuild unless the object file exists and is newer than the source:
|
|
.PP
|
|
.RS 4
|
|
.EX
|
|
$ [ main.o \-nt main.c ] || gcc \-c main.c
|
|
.EE
|
|
.RE
|
|
.PP
|
|
.B test
|
|
cannot match a string against a pattern.
|
|
Use the shell's
|
|
.B case
|
|
command instead:
|
|
.PP
|
|
.RS 4
|
|
.EX
|
|
$ name=main.c
|
|
$ case $name in *.c) echo C source;; esac
|
|
C source
|
|
.EE
|
|
.RE
|
|
.SH NOTES
|
|
Differences from GNU
|
|
.B test
|
|
and bash:
|
|
.IP \(bu 2
|
|
.B \-nt
|
|
and
|
|
.B \-ot
|
|
are false unless both files exist, where bash makes
|
|
.B \-nt
|
|
true when only the first exists, and
|
|
.B \-ot
|
|
when only the second does.
|
|
They compare whole seconds.
|
|
.IP \(bu 2
|
|
The bash unary operators
|
|
.BR \-a ,
|
|
.BR \-N ,
|
|
.BR \-o ,
|
|
.BR \-R ,
|
|
and
|
|
.B \-v
|
|
are not supported.
|
|
.IP \(bu 2
|
|
There is no
|
|
.B [[
|
|
command, so bash's pattern and regular expression tests are not
|
|
available.
|
|
busybox-w32 builds
|
|
.B [[
|
|
into its shell for bash compatibility, and w64devkit leaves it out.
|
|
To match a pattern, use the shell's
|
|
.B case
|
|
command.
|
|
.PP
|
|
File names follow Windows rules.
|
|
Either slash separates components, drive letters work, and a leading
|
|
slash is the root of the current drive.
|
|
MSYS-style names such as
|
|
.I /c/Users
|
|
are not translated.
|
|
The device names
|
|
.B NUL
|
|
and
|
|
.B CON
|
|
do not exist: use
|
|
.IR /dev/null .
|
|
The
|
|
.I /dev
|
|
names under
|
|
.B \-c
|
|
are recognized by spelling, not by looking at the file system, so
|
|
they work from any directory and drive but
|
|
.I C:/dev/null
|
|
does not.
|
|
Names may contain any Unicode character on Windows 10 version 1903 and
|
|
later; see
|
|
.BR busybox (1).
|
|
.PP
|
|
Permissions come from a Unix-style mode that busybox builds for each file
|
|
from its attributes and name, and tests against the effective user ID.
|
|
Any file is readable.
|
|
A directory is writable, and so is a regular file without the read-only
|
|
attribute.
|
|
A directory is executable, and so is a regular file whose name ends in
|
|
.BR .com ,
|
|
.BR .exe ,
|
|
.BR .sh ,
|
|
.BR .bat ,
|
|
or
|
|
.B .cmd
|
|
in any case, or whose contents begin with
|
|
.B #!\&
|
|
or are a PE executable that is not a DLL.
|
|
For other names, except
|
|
.BR .dll ,
|
|
that check reads the start of the file, unless its data is not stored
|
|
locally.
|
|
A file owned by someone else is writable only if, in addition, its
|
|
access control list grants the user full access or the umask lacks the
|
|
bit
|
|
.B 002
|
|
that removes write permission for others; see
|
|
.BR BB_UMASK .
|
|
A file whose security information cannot be opened is none of readable,
|
|
writable, or executable.
|
|
.PP
|
|
An elevated administrator process has user ID 0, as
|
|
.BR id (1)
|
|
shows, and then, as root would on Unix,
|
|
.B \-r
|
|
and
|
|
.B \-w
|
|
are true for every file that can be examined, and
|
|
.B \-x
|
|
for every executable one.
|
|
The user ID is otherwise 4095.
|
|
.B \-O
|
|
compares the effective user ID with the owner found in the file's
|
|
security information, which busybox maps to a number: 4095 for the
|
|
current user, the relative ID of another local or domain account if
|
|
that is from 500 to 4094, and 0 for any other owner, such as SYSTEM or
|
|
the Administrators group, or when the information cannot be read.
|
|
An emulated device, or a file whose data is not stored locally, belongs
|
|
to the current user.
|
|
So an elevated process owns the files that map to 0, and not those owned
|
|
by its own account.
|
|
.PP
|
|
.B \-ef
|
|
compares the volume serial number and file index, so hard links, and a
|
|
link or junction and its target, are the same file.
|
|
The emulated devices, files whose data is not stored locally, and any
|
|
other files whose identity cannot be read all compare equal to each
|
|
other.
|
|
.PP
|
|
When the whole expression, after any leading
|
|
.BR ! ,
|
|
is a unary test in parentheses, such as
|
|
.B ( \-n x )
|
|
or
|
|
.BR "! ( \-d dir )" ,
|
|
it fails with
|
|
.B closing paren expected
|
|
and status 2.
|
|
So does a unary operator or
|
|
.B !\&
|
|
alone in parentheses, such as
|
|
.BR "( \-n )" ,
|
|
which POSIX makes a test of a non-empty string.
|
|
Leave out the parentheses.
|
|
A longer expression, such as
|
|
.B ( ! \-d dir )
|
|
or
|
|
.BR "( \-n x ) \-a y" ,
|
|
is not affected.
|
|
.SH SEE ALSO
|
|
.BR busybox (1),
|
|
.BR expr (1),
|
|
.BR find (1),
|
|
.BR id (1),
|
|
.BR sh (1)
|