mirror of
https://https.git.savannah.gnu.org/git/findutils.git
synced 2026-01-26 15:39:06 +00:00
find: add -files0-from option
* bootstrap.conf (gnulib_modules): Add argv-iter and same-inode. * find/defs.h (struct options): Add files0_from and ok_prompt_stdin members. * find/ftsfind.c (argv-iter.h, same-inode.h, xalloc.h): Add #include for gnulib headers. (process_all_startpoints): Change loop over starting point arguments to a loop using the argv_iter gnulib module. * find/parser.c (parse_table): Add option. (parse_files0_from): Declare and define function. (insert_exec_ok): Set options flag ok_prompt_stdin to true for the -ok and -okdir action. * find/util.c (usage): Add new option. (set_option_defaults): Initialize new struct members. * doc/find.texi (node Starting points): Add new section describing the regular processing of starting points, and that with the new option. Also mention in the description of -ok and -okdir that they conflict with the new option. * find/find.1: Document the new option here as well. * tests/find/files0-from.sh: Add test. * tests/local.mk (all_tests): Reference it. * NEWS (New features in find): Mention the new option.
This commit is contained in:
parent
9ec8970ce7
commit
11576f4e6a
7
NEWS
7
NEWS
@ -2,6 +2,13 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout)
|
||||
|
||||
* Noteworthy changes in release ?.? (????-??-??) [?]
|
||||
|
||||
** New features in find
|
||||
|
||||
find now supports the -files0-from option to be able to safely pass an
|
||||
arbitrary number of starting points to the tool. The option requires a file
|
||||
name as argument, or "-" to read from standard input. The entries in that
|
||||
file have to be separated by NUL characters.
|
||||
|
||||
** Changes in locate / updatedb
|
||||
|
||||
update now skips (fuse-mounted) s3fs filesystems by default,
|
||||
|
||||
@ -74,6 +74,7 @@ gnulib_modules="
|
||||
alloca
|
||||
areadlinkat
|
||||
argmatch
|
||||
argv-iter
|
||||
assert
|
||||
byteswap
|
||||
c-strcasestr
|
||||
@ -143,6 +144,7 @@ gnulib_modules="
|
||||
regex
|
||||
rpmatch
|
||||
safe-read
|
||||
same-inode
|
||||
save-cwd
|
||||
savedir
|
||||
selinux-at
|
||||
|
||||
@ -287,6 +287,7 @@ more information about the matching files.
|
||||
|
||||
@menu
|
||||
* find Expressions::
|
||||
* Starting points::
|
||||
* Name::
|
||||
* Links::
|
||||
* Time::
|
||||
@ -357,6 +358,90 @@ for exactly @var{n}.
|
||||
@end table
|
||||
|
||||
|
||||
@node Starting points
|
||||
@section Starting points
|
||||
|
||||
GNU @code{find} searches the directory tree rooted at each given starting-point
|
||||
by evaluating the given expression from left to right, according to the
|
||||
rules of operator precedence, until the outcome is known (the left hand side
|
||||
is false for @samp{and} operations, true for @samp{or}), at which point
|
||||
@code{find} moves on to the next file name.
|
||||
|
||||
If no starting-point is specified, the current directory @samp{.} is assumed.
|
||||
|
||||
A double dash @samp{--} could theoretically be used to signal that any remaining
|
||||
arguments are not options, but this does not really work due to the way
|
||||
@code{find} determines the end of the list of starting point arguments:
|
||||
it does that by reading until an expression argument comes (which also starts
|
||||
with a @samp{-}).
|
||||
Now, if a starting point argument would begin with a @samp{-}, then @code{find}
|
||||
would treat it as expression argument instead.
|
||||
Thus, to ensure that all start points are taken as such, and especially to
|
||||
prevent that wildcard patterns expanded by the calling shell are not mistakenly
|
||||
treated as expression arguments, it is generally safer to prefix wildcards
|
||||
or dubious path names with either @samp{./}, or to use absolute path names
|
||||
starting with @samp{/}.
|
||||
|
||||
Alternatively, it is generally safe though non-portable to use the GNU option
|
||||
@samp{-files0-from} to pass arbitrary starting points to @code{find}.
|
||||
|
||||
@deffn Option -files0-from file
|
||||
@deffnx Option -files0-from file
|
||||
|
||||
Read the starting points from @file{file} instead of getting them on the
|
||||
command line.
|
||||
In contrast to the known limitations of passing starting points via arguments
|
||||
on the command line, namely the limitation of the amount of file names,
|
||||
and the inherent ambiguity of file names clashing with option names,
|
||||
using this option allows to safely pass an arbitrary number of starting points
|
||||
to @code{find}.
|
||||
|
||||
Using this option and passing starting points on the command line is mutually
|
||||
exclusive, and is therefore not allowed at the same time.
|
||||
|
||||
The @file{file} argument is mandatory.
|
||||
One can use @samp{-files0-from -} to read the list of starting points from the
|
||||
standard input stream, and e.g. from a pipe.
|
||||
In this case, the actions @samp{-ok} and @samp{-okdir} are not allowed,
|
||||
because they would obviously interfere with reading from standard input
|
||||
in order to get a user confirmation.
|
||||
|
||||
The starting points in @file{file} have to be separated by ASCII NUL characters.
|
||||
Two consecutive NUL characters, i.e., a starting point with a Zero-length
|
||||
file name is not allowed and will lead to an error diagnostic followed by
|
||||
a non-Zero exit code later.
|
||||
The given @file{file} has to contain at least one starting point,
|
||||
i.e., an empty input file will be diagnosed as well.
|
||||
|
||||
The processing of the starting points is otherwise as usual, e.g. @code{find}
|
||||
will recurse into subdirectories unless otherwise prevented.
|
||||
To process only the starting points, one can additionally pass @samp{-maxdepth 0}.
|
||||
|
||||
Further notes:
|
||||
if a file is listed more than once in the input file, it is unspecified
|
||||
whether it is visited more than once.
|
||||
If the @file{file} is mutated during the operation of @code{find}, the result
|
||||
is unspecified as well.
|
||||
Finally, the seek position within the named @samp{file} at the time @code{find}
|
||||
exits, be it with @samp{-quit} or in any other way, is also unspecified.
|
||||
By "unspecified" here is meant that it may or may not work or do any specific
|
||||
thing, and that the behavior may change from platform to platform, or from
|
||||
findutils release to release.
|
||||
|
||||
Example:
|
||||
Given that another program @code{proggy} pre-filters and creates a huge
|
||||
NUL-separated list of files, process those as starting points, and find
|
||||
all regular, empty files among them:
|
||||
@example
|
||||
$ proggy | find -files0-from - -maxdepth 0 -type f -empty
|
||||
@end example
|
||||
The use of @samp{-files0-from -} means to read the names of the starting points
|
||||
from standard input, i.e., from the pipe; and @samp{-maxdepth 0} ensures that
|
||||
only explicitly those entries are examined without recursing into directories
|
||||
(in the case one of the starting points is one).
|
||||
@end deffn
|
||||
|
||||
|
||||
@node Name
|
||||
@section Name
|
||||
|
||||
@ -2822,6 +2907,8 @@ If the user does not agree to run the command, just return false.
|
||||
Otherwise, run it, with standard input redirected from
|
||||
@file{/dev/null}.
|
||||
|
||||
This action may not be specified together with the @samp{-files0-from} option.
|
||||
|
||||
The response to the prompt is matched against a pair of regular
|
||||
expressions to determine if it is a yes or no response. These regular
|
||||
expressions are obtained from the system (@code{nl_langinfo} items
|
||||
@ -2844,6 +2931,8 @@ is expanded to a relative path starting with the name of one of the
|
||||
starting directories, rather than just the basename of the matched
|
||||
file. If the command is run, its standard input is redirected from
|
||||
@file{/dev/null}.
|
||||
|
||||
This action may not be specified together with the @samp{-files0-from} option.
|
||||
@end deffn
|
||||
|
||||
When processing multiple files with a single command, to query the
|
||||
|
||||
@ -636,6 +636,12 @@ struct options
|
||||
/* How should we quote filenames in error messages and so forth?
|
||||
*/
|
||||
enum quoting_style err_quoting_style;
|
||||
|
||||
/* Read starting points from FILE (instead of argv). */
|
||||
const char *files0_from;
|
||||
|
||||
/* True if actions like -ok, -okdir need a user confirmation via stdin. */
|
||||
bool ok_prompt_stdin;
|
||||
};
|
||||
|
||||
|
||||
|
||||
88
find/find.1
88
find/find.1
@ -79,6 +79,11 @@ prevent that wildcard patterns expanded by the calling shell are not mistakenly
|
||||
treated as expression arguments, it is generally safer to prefix wildcards or
|
||||
dubious path names with either `./' or to use absolute path names starting
|
||||
with '/'.
|
||||
Alternatively, it is generally safe though non-portable to use the GNU option
|
||||
.B \-files0\-from
|
||||
to pass arbitrary starting points to
|
||||
.BR find .
|
||||
|
||||
.IP \-P
|
||||
Never follow symbolic links. This is the default behaviour. When
|
||||
.B find
|
||||
@ -506,6 +511,59 @@ Process each directory's contents before the directory itself. The
|
||||
\-delete action also implies
|
||||
.BR \-depth .
|
||||
|
||||
.IP "\-files0\-from \fIfile\fR"
|
||||
Read the starting points from \fIfile\fR instead of getting them on the
|
||||
command line.
|
||||
In contrast to the known limitations of passing starting points via arguments
|
||||
on the command line, namely the limitation of the amount of file names,
|
||||
and the inherent ambiguity of file names clashing with option names,
|
||||
using this option allows to safely pass an arbitrary number of starting points
|
||||
to \fBfind\fR.
|
||||
|
||||
Using this option and passing starting points on the command line is mutually
|
||||
exclusive, and is therefore not allowed at the same time.
|
||||
|
||||
The \fIfile\fR argument is mandatory.
|
||||
One can use
|
||||
.B \-files0\-from\ \-
|
||||
to read the list of starting points from the \fIstandard input\fR stream,
|
||||
and e.g. from a pipe.
|
||||
In this case, the actions
|
||||
.B \-ok
|
||||
and
|
||||
.B \-okdir
|
||||
are not allowed, because they would obviously interfere with reading from
|
||||
\fIstandard input\fR in order to get a user confirmation.
|
||||
|
||||
The starting points in \fIfile\fR have to be separated by ASCII NUL characters.
|
||||
Two consecutive NUL characters, i.e., a starting point with a Zero-length
|
||||
file name is not allowed and will lead to an error diagnostic followed by
|
||||
a non-Zero exit code later.
|
||||
The given \fIfile\fR has to contain at least one starting point,
|
||||
i.e., an empty input file will be diagnosed as well.
|
||||
|
||||
The processing of the starting points is otherwise as usual, e.g.
|
||||
.B find
|
||||
will recurse into subdirectories unless otherwise prevented.
|
||||
To process only the starting points, one can additionally pass
|
||||
.BR \-maxdepth\ 0 .
|
||||
|
||||
Further notes:
|
||||
if a file is listed more than once in the input file, it is unspecified
|
||||
whether it is visited more than once.
|
||||
If the \fIfile\fR is mutated during the operation of
|
||||
.BR find ,
|
||||
the result is unspecified as well.
|
||||
Finally, the seek position within the named \fIfile\fR at the time
|
||||
.B find
|
||||
exits, be it with
|
||||
.B \-quit
|
||||
or in any other way, is also unspecified.
|
||||
By "unspecified" here is meant that it may or may not work or do any specific
|
||||
thing, and that the behavior may change from platform to platform, or from
|
||||
.B findutils
|
||||
release to release.
|
||||
|
||||
.IP "\-help, \-\-help"
|
||||
Print a summary of the command-line usage of
|
||||
.B find
|
||||
@ -1324,6 +1382,9 @@ but ask the user first. If the user agrees, run the command. Otherwise
|
||||
just return false. If the command is run, its standard input is redirected
|
||||
from
|
||||
.IR /dev/null .
|
||||
This action may not be specified together with the
|
||||
.B \-files0\-from
|
||||
option.
|
||||
|
||||
.IP
|
||||
The response to the prompt is matched against a pair of regular
|
||||
@ -1353,6 +1414,10 @@ but ask the user first in the same way as for
|
||||
If the user does not agree, just return false.
|
||||
If the command is run, its standard input is redirected from
|
||||
.IR /dev/null .
|
||||
This action may not be specified together with the
|
||||
.B \-files0\-from
|
||||
option.
|
||||
|
||||
|
||||
.IP \-print
|
||||
True; print the full file name on the standard output, followed by a
|
||||
@ -2159,7 +2224,27 @@ traverses the hierarchy printing the matching filenames, and the time the
|
||||
process executed by
|
||||
.B xargs
|
||||
works with that file.
|
||||
|
||||
.
|
||||
.SS Processing arbitrary starting points
|
||||
.IP \[bu]
|
||||
Given that another program \fIproggy\fR pre-filters and creates a huge
|
||||
NUL-separated list of files, process those as starting points, and find
|
||||
all regular, empty files among them:
|
||||
.nf
|
||||
\&
|
||||
.in +4m
|
||||
.B $ proggy | find \-files0\-from \- \-maxdepth 0 \-type f \-empty
|
||||
.in
|
||||
\&
|
||||
.fi
|
||||
The use of
|
||||
.B `\-files0\-from\ \-`
|
||||
means to read the names of the starting points from \fIstandard input\fR,
|
||||
i.e., from the pipe; and
|
||||
.B \-maxdepth\ 0
|
||||
ensures that only explicitly those entries are examined without recursing
|
||||
into directories (in the case one of the starting points is one).
|
||||
.
|
||||
.SS
|
||||
Executing a command for each file
|
||||
.IP \[bu]
|
||||
@ -2524,6 +2609,7 @@ exit status was unaffected by the failure of
|
||||
.TS
|
||||
l l l .
|
||||
Feature Added in Also occurs in
|
||||
\-files0\-from 4.9.0
|
||||
\-newerXY 4.3.3 BSD
|
||||
\-D 4.3.1
|
||||
\-O 4.3.1
|
||||
|
||||
172
find/ftsfind.c
172
find/ftsfind.c
@ -38,6 +38,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
/* gnulib headers. */
|
||||
#include "argv-iter.h"
|
||||
#include "cloexec.h"
|
||||
#include "closeout.h"
|
||||
#include "error.h"
|
||||
@ -45,8 +46,10 @@
|
||||
#include "intprops.h"
|
||||
#include "progname.h"
|
||||
#include "quotearg.h"
|
||||
#include "same-inode.h"
|
||||
#include "save-cwd.h"
|
||||
#include "xgetcwd.h"
|
||||
#include "xalloc.h"
|
||||
|
||||
/* find headers. */
|
||||
#include "defs.h"
|
||||
@ -563,30 +566,163 @@ find (char *arg)
|
||||
static bool
|
||||
process_all_startpoints (int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
bool empty = true;
|
||||
/* Did the user pass starting points on the command line? */
|
||||
bool argv_starting_points = 0 < argc && !looks_like_expression (argv[0], true);
|
||||
|
||||
/* figure out how many start points there are */
|
||||
for (i = 0; i < argc && !looks_like_expression (argv[i], true); i++)
|
||||
FILE *stream = NULL;
|
||||
char const* files0_filename_quoted = NULL;
|
||||
|
||||
struct argv_iterator *ai;
|
||||
if (options.files0_from)
|
||||
{
|
||||
empty = false;
|
||||
state.starting_path_length = strlen (argv[i]); /* TODO: is this redundant? */
|
||||
if (!find (argv[i]))
|
||||
return false;
|
||||
/* Option -files0-from must not be combined with passing starting points
|
||||
* on the command line. */
|
||||
if (argv_starting_points)
|
||||
{
|
||||
error (0, 0, _("extra operand %s"), safely_quote_err_filename (0, argv[0]));
|
||||
die (EXIT_FAILURE, 0, "%s",
|
||||
_("file operands cannot be combined with -files0-from"));
|
||||
}
|
||||
|
||||
if (0 == strcmp (options.files0_from, "-"))
|
||||
{
|
||||
/* Option -files0-from with argument "-" (=stdin) must not be combined
|
||||
* with the -ok, -okdir actions: getting the user confirmation would
|
||||
* mess with stdin. */
|
||||
if (options.ok_prompt_stdin)
|
||||
{
|
||||
die (EXIT_FAILURE, 0, "%s\n",
|
||||
_("option -files0-from reading from standard input"
|
||||
" cannot be combined with -ok, -okdir"));
|
||||
}
|
||||
files0_filename_quoted = safely_quote_err_filename (0, _("(standard input)"));
|
||||
stream = stdin;
|
||||
}
|
||||
else
|
||||
{
|
||||
files0_filename_quoted = safely_quote_err_filename (0, options.files0_from);
|
||||
stream = fopen (options.files0_from, "r");
|
||||
if (stream == NULL)
|
||||
die (EXIT_FAILURE, errno, _("cannot open %s for reading"),
|
||||
files0_filename_quoted);
|
||||
|
||||
const int fd = fileno (stream);
|
||||
assert (fd >= 0);
|
||||
if (options.ok_prompt_stdin)
|
||||
{
|
||||
/* Check if the given file is associated to the same stream as
|
||||
* standard input - which is not allowed with -ok, -okdir. This
|
||||
* is the case with special device names symlinks for stdin like
|
||||
* $ find -files0-from /dev/stdin -ok
|
||||
* or when the given FILE is also associated to stdin:
|
||||
* $ find -files0-from FILE -ok < FILE
|
||||
*/
|
||||
struct stat sb1, sb2;
|
||||
if (fstat (fd, &sb1) == 0 && fstat (STDIN_FILENO, &sb2) == 0
|
||||
&& SAME_INODE (sb1, sb2))
|
||||
{
|
||||
die (EXIT_FAILURE, 0, "%s: %s\n",
|
||||
_("option -files0-from: standard input must not refer"
|
||||
" to the same file when combined with -ok, -okdir"),
|
||||
files0_filename_quoted);
|
||||
}
|
||||
}
|
||||
set_cloexec_flag (fd, true);
|
||||
}
|
||||
ai = argv_iter_init_stream (stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!argv_starting_points)
|
||||
{
|
||||
/* If no starting points are given on the comman line, then
|
||||
* fall back to processing the current directory, i.e., ".".
|
||||
* We use a temporary variable here because some actions modify
|
||||
* the path temporarily. Hence if we use a string constant,
|
||||
* we get a coredump. The best example of this is if we say
|
||||
* "find -printf %H" (note, not "find . -printf %H").
|
||||
*/
|
||||
char defaultpath[2] = ".";
|
||||
return find (defaultpath);
|
||||
}
|
||||
|
||||
/* Process the starting point(s) from the command line. */
|
||||
ai = argv_iter_init_argv (argv);
|
||||
}
|
||||
|
||||
if (empty)
|
||||
if (!ai)
|
||||
xalloc_die ();
|
||||
|
||||
bool ok = true;
|
||||
while (true)
|
||||
{
|
||||
/*
|
||||
* We use a temporary variable here because some actions modify
|
||||
* the path temporarily. Hence if we use a string constant,
|
||||
* we get a coredump. The best example of this is if we say
|
||||
* "find -printf %H" (note, not "find . -printf %H").
|
||||
*/
|
||||
char defaultpath[2] = ".";
|
||||
return find (defaultpath);
|
||||
enum argv_iter_err ai_err;
|
||||
char *file_name = argv_iter (ai, &ai_err);
|
||||
if (!file_name)
|
||||
{
|
||||
switch (ai_err)
|
||||
{
|
||||
case AI_ERR_EOF:
|
||||
goto argv_iter_done;
|
||||
case AI_ERR_READ: /* may only happen with -files0-from */
|
||||
error (0, errno, _("%s: read error"), files0_filename_quoted);
|
||||
state.exit_status = 1;
|
||||
ok = false;
|
||||
goto argv_iter_done;
|
||||
case AI_ERR_MEM:
|
||||
xalloc_die ();
|
||||
default:
|
||||
assert (!"unexpected error code from argv_iter");
|
||||
}
|
||||
}
|
||||
/* Report and skip any empty file names before invoking fts.
|
||||
This works around a glitch in fts, which fails immediately
|
||||
(without looking at the other file names) when given an empty
|
||||
file name. */
|
||||
if (!file_name[0])
|
||||
{
|
||||
/* Diagnose a zero-length file name. When it's one
|
||||
among many, knowing the record number may help. */
|
||||
if (options.files0_from == NULL)
|
||||
error (0, ENOENT, "%s", safely_quote_err_filename (0, file_name));
|
||||
else
|
||||
{
|
||||
/* Using the standard 'filename:line-number:' prefix here is
|
||||
not totally appropriate, since NUL is the separator, not NL,
|
||||
but it might be better than nothing. */
|
||||
unsigned long int file_number = argv_iter_n_args (ai);
|
||||
error (0, 0, "%s:%lu: %s", files0_filename_quoted, file_number,
|
||||
_("invalid zero-length file name"));
|
||||
}
|
||||
state.exit_status = 1;
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Terminate loop when processing the start points from command line,
|
||||
and reaching the first expression. */
|
||||
if (!options.files0_from && looks_like_expression (file_name, true))
|
||||
break;
|
||||
|
||||
state.starting_path_length = strlen (file_name); /* TODO: is this redundant? */
|
||||
if (!find (file_name))
|
||||
{
|
||||
ok = false;
|
||||
goto argv_iter_done;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
argv_iter_done:
|
||||
|
||||
if (ok && options.files0_from && argv_iter_n_args (ai) <= 0)
|
||||
die (EXIT_FAILURE, 0, _("file with starting points is empty: %s"),
|
||||
files0_filename_quoted);
|
||||
|
||||
argv_iter_free (ai);
|
||||
|
||||
if (ok && options.files0_from && (ferror (stream) || fclose (stream) != 0))
|
||||
die (EXIT_FAILURE, 0, _("error reading %s"), files0_filename_quoted);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@ static bool parse_empty (const struct parser_table*, char *argv[], int *
|
||||
static bool parse_exec (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
static bool parse_execdir (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
static bool parse_false (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
static bool parse_files0_from (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
static bool parse_fls (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
static bool parse_fprintf (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
static bool parse_follow (const struct parser_table*, char *argv[], int *arg_ptr);
|
||||
@ -241,6 +242,7 @@ static struct parser_table const parse_table[] =
|
||||
{ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */
|
||||
{ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */
|
||||
PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
|
||||
PARSE_OPTION ("files0-from", files0_from), /* GNU */
|
||||
PARSE_ACTION ("fls", fls), /* GNU */
|
||||
PARSE_POSOPT ("follow", follow), /* GNU, Unix */
|
||||
PARSE_ACTION ("fprint", fprint), /* GNU */
|
||||
@ -963,6 +965,18 @@ parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
|
||||
return insert_false ();
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_files0_from (const struct parser_table* entry, char **argv, int *arg_ptr)
|
||||
{
|
||||
const char *filename;
|
||||
if (collect_arg (argv, arg_ptr, &filename))
|
||||
{
|
||||
options.files0_from = filename;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
insert_fls (const struct parser_table* entry, const char *filename)
|
||||
{
|
||||
@ -2903,6 +2917,8 @@ insert_exec_ok (const char *action,
|
||||
else
|
||||
{
|
||||
allow_plus = false;
|
||||
/* The -ok* family need user confirmations via stdin. */
|
||||
options.ok_prompt_stdin = true;
|
||||
/* If find reads stdin (i.e. for -ok and similar), close stdin
|
||||
* in the child to prevent some script from consuming the output
|
||||
* intended for find.
|
||||
|
||||
@ -180,8 +180,9 @@ operators (decreasing precedence; -and is implicit where no others are given):\n
|
||||
HTL (_("\
|
||||
positional options (always true): -daystart -follow -regextype\n\n\
|
||||
normal options (always true, specified before other expressions):\n\
|
||||
-depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
|
||||
--version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
|
||||
-depth --help -files0-from FILE -maxdepth LEVELS -mindepth LEVELS\n\
|
||||
-mount -noleaf --version -xdev -ignore_readdir_race\n\
|
||||
-noignore_readdir_race\n"));
|
||||
HTL (_("\
|
||||
tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
|
||||
-cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
|
||||
@ -1073,6 +1074,9 @@ set_option_defaults (struct options *p)
|
||||
set_follow_state (SYMLINK_NEVER_DEREF); /* The default is equivalent to -P. */
|
||||
|
||||
p->err_quoting_style = locale_quoting_style;
|
||||
|
||||
p->files0_from = NULL;
|
||||
p->ok_prompt_stdin = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
170
tests/find/files0-from.sh
Executable file
170
tests/find/files0-from.sh
Executable file
@ -0,0 +1,170 @@
|
||||
#!/bin/sh
|
||||
# Exercise -files0-from option.
|
||||
|
||||
# Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/tests/init.sh"; fu_path_prepend_
|
||||
print_ver_ find
|
||||
|
||||
# Option -files0-from requires a file name argument.
|
||||
returns_ 1 find -files0-from > out 2> err \
|
||||
&& grep 'missing argument.*files0' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# Option -files0-from must not be combined with passing starting points on
|
||||
# the command line.
|
||||
returns_ 1 find OFFENDING -files0-from FILE > out 2> err \
|
||||
&& grep 'extra operand .*OFFENDING' err \
|
||||
&& grep 'file operands cannot be combined with -files0-from' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# Process "." as default when starting points neither passed via -files0-from
|
||||
# nor on the command line.
|
||||
printf "%s\n" '.' > exp || framework_failure_
|
||||
find -maxdepth 0 > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
# Option -files0-from with argument "-" (=stdin) must not be combined with
|
||||
# the -ok action: getting the user confirmation would mess with stdin.
|
||||
returns_ 1 find -files0-from - -ok echo '{}' ';' < /dev/null > out 2> err \
|
||||
&& grep 'files0.* standard input .*cannot be combined with .*ok' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# Option -files0-from with argument "-" (=stdin) must not be combined with
|
||||
# the -okdir action: getting the user confirmation would mess with stdin.
|
||||
returns_ 1 find -files0-from - -okdir echo '{}' ';' < /dev/null > out 2> err \
|
||||
&& grep 'files0.* standard input .*cannot be combined with .*ok' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# File argument of -files0-from option must not refer to the same file as stdin.
|
||||
printf '.' > in || framework_failure_
|
||||
returns_ 1 find -files0-from in -ok echo '{}' ';' < in > out 2> err \
|
||||
&& grep 'files0.* standard input .*same file .*with -ok, -okdir' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# Likewise via a symlink.
|
||||
if ln -s in inlink; then
|
||||
returns_ 1 find -files0-from inlink -ok echo '{}' ';' < in > out 2> err \
|
||||
&& grep 'files0.* standard input .*same file .*with -ok, -okdir' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
# ... and vice versa.
|
||||
returns_ 1 find -files0-from in -ok echo '{}' ';' < inlink > out 2> err \
|
||||
&& grep 'files0.* standard input .*same file .*with -ok, -okdir' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
fi
|
||||
|
||||
# Likewise when the system provides the name '/dev/stdin'.
|
||||
if ls /dev/stdin >/dev/null 2>&1; then
|
||||
returns_ 1 find -files0-from /dev/stdin -ok echo '{}' ';' < in > out 2> err \
|
||||
&& grep 'files0.* standard input .*same file .*with -ok, -okdir' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
fi
|
||||
|
||||
# Exercise a non-existing file.
|
||||
returns_ 1 find -files0-from ENOENT > out 2> err \
|
||||
&& grep 'cannot open .ENOENT. for reading: No such' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# Exercise a file which cannot be opened.
|
||||
# The shadow(5) file seems to be a good choice for reasonable coverage.
|
||||
f='/etc/shadow'
|
||||
if test -e $f && test '!' -r $f; then
|
||||
returns_ 1 find -files0-from $f > out 2> err \
|
||||
&& grep 'cannot open .* for reading: Permission denied' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
fi
|
||||
|
||||
# Exercise a directory argument.
|
||||
returns_ 1 find -files0-from / > out 2> err \
|
||||
&& grep 'read error' err \
|
||||
|| { grep . out err; fail=1; }
|
||||
|
||||
# Exercise an empty input file.
|
||||
returns_ 1 find -files0-from /dev/null > out 2> err || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
grep 'file with starting points is empty:' err || fail=1
|
||||
|
||||
# Likewise via stdin.
|
||||
returns_ 1 find -files0-from - < /dev/null > out 2> err || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
grep 'file with starting points is empty:.*standard input' err || fail=1
|
||||
|
||||
# Likewise via a pipe on stdin.
|
||||
cat /dev/null | returns_ 1 find -files0-from - > out 2> err || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
grep 'file with starting points is empty:.*standard input' err || fail=1
|
||||
|
||||
# Now a regular case: 2 files: expect the same output.
|
||||
touch a b || framework_failure_
|
||||
printf '%s\0' a b > in || framework_failure_
|
||||
tr '\0' '\n' < in > exp || framework_failure_
|
||||
find -files0-from in -print > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
# Demonstrate that -files0-from accepts file names which would otherwise be
|
||||
# rejected because they are recognized as test or action.
|
||||
touch ./-print ./-mtime ./-size || framework_failure_
|
||||
printf '%s\0' _print _mtime _size | tr '_' '-' > in || framework_failure_
|
||||
tr '\0' '\n' < in > exp || framework_failure_
|
||||
find -files0-from in -printf '%p\n' > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
# Zero-length file name on position 2, once per stdin ...
|
||||
printf '%s\n' a b > exp || framework_failure_
|
||||
printf '%s\0' a '' b \
|
||||
| tee file \
|
||||
| returns_ 1 find -files0-from - -print > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
grep '(standard input).:2: invalid zero-length file name' err || fail=1
|
||||
# ... and once per file.
|
||||
returns_ 1 find -files0-from file -print > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
grep 'file.:2: invalid zero-length file name' err || fail=1
|
||||
|
||||
# Non-existing file name.
|
||||
printf '%s\0' a ENOENT b \
|
||||
| returns_ 1 find -files0-from - -print > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
grep 'ENOENT' err || fail=1
|
||||
|
||||
# Demonstrate (the usual!) recursion ...
|
||||
mkdir d1 d1/d2 d1/d2/d3 && touch d1/d2/d3/file || framework_failure_
|
||||
printf '%s\n' d1 d1/d2 d1/d2/d3 d1/d2/d3/file > exp || framework_failure_
|
||||
printf 'd1' \
|
||||
| find -files0-from - > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
# ... and how to avoid recursion with -maxdepth 0.
|
||||
find d1 -print0 \
|
||||
| find -files0-from - -maxdepth 0 > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
# Large input with files.
|
||||
# Create file 'exp' with many times its own name as content, NUL-separated;
|
||||
# and let find(1) check that it worked (4x in bytes).
|
||||
yes exp | head -n 100000 | tr '\n' '\0' > exp \
|
||||
&& find exp -size 400000c | grep . || framework_failure_
|
||||
# Run the test.
|
||||
find -files0-from exp > out 2> err || fail=1
|
||||
# ... and verify.
|
||||
test $( wc -l < out ) = 100000 || fail=1
|
||||
find out -size 400000c | grep . || fail=1
|
||||
|
||||
Exit $fail
|
||||
@ -114,6 +114,7 @@ all_tests = \
|
||||
tests/find/printf_inode.sh \
|
||||
tests/find/execdir-fd-leak.sh \
|
||||
tests/find/exec-plus-last-file.sh \
|
||||
tests/find/files0-from.sh \
|
||||
tests/find/refuse-noop.sh \
|
||||
tests/find/debug-missing-arg.sh \
|
||||
tests/find/used.sh \
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user