From 8b0e0e11972da311a68d8f4c617129fb7c4c6897 Mon Sep 17 00:00:00 2001 From: jay Date: Sun, 21 Nov 2004 17:57:50 +0000 Subject: [PATCH] Implemented xargs --arg-file --- NEWS | 10 ++++++++- doc/find.texi | 58 +++++++++++++++++++++++++++++++++++++++++++-------- xargs/xargs.1 | 12 ++++++++++- xargs/xargs.c | 66 ++++++++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 116 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 4ccc5ba..c52a97b 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,18 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) +* Major changes in release 4.2.7 +** Bug Fixes +*** Avoid trying to link against -lsun on UNICOS, which doesn't need it or + have it. +*** Bugfix to the findutils 4.2.6 automount handling (which hadn't been enabled + on Solaris). + * Major changes in release 4.2.6 ** Bug Fixes *** find now copes rather better when a directory appears to change just as it is about to start examining it, which happens with automount. This is because automount mounts filesystems as you change - directory into them. This should resolve Savannah bugs #3998, #9043. + directory into them. This should resolve Savannah bugs #3998, + #9043. * Major changes in release 4.2.5 ** Functionality Changes diff --git a/doc/find.texi b/doc/find.texi index 79c0017..993d546 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -1564,12 +1564,12 @@ command, which is invoked like this: xargs @r{[}@var{option}@dots{}@r{]} @r{[}@var{command} @r{[}@var{initial-arguments}@r{]}@r{]} @end example -@code{xargs} reads arguments from the standard input, delimited by -blanks (which can be protected with double or single quotes or a -backslash) or newlines. It executes the @var{command} (default is -@file{/bin/echo}) one or more times with any @var{initial-arguments} -followed by arguments read from standard input. Blank lines on the -standard input are ignored. +@code{xargs} normally reads arguments from the standard input. These +arguments are delimited by blanks (which can be protected with double +or single quotes or a backslash) or newlines. It executes the +@var{command} (default is @file{/bin/echo}) one or more times with any +@var{initial-arguments} followed by arguments read from standard +input. Blank lines on the standard input are ignored. Instead of blank-delimited names, it is safer to use @samp{find -print0} or @samp{find -fprint0} and process the output by giving the @samp{-0} @@ -1595,7 +1595,8 @@ find $HOME -name '*.c' -print | xargs grep -l sprintf However, if the command needs to have its standard input be a terminal (@code{less}, for example), you have to use the shell command -substitution method. +substitution method or use the @samp{--arg-file} option of +@code{xargs}. The @code{xargs} command will process all its input, building command lines and executing them, unless one of the commands exits with a @@ -1689,6 +1690,25 @@ As of findutils version 4.2.4, the @code{locate} program also has a @samp{--null} option which does the same thing. For similarity with @code{xargs}, the short form of the option @samp{-0} can also be used. +If you want to be able to handle file names safely but need to run +commands which want to be connected to a terminal on their input, you +can use the @samp{--arg-file} option to @code{xargs} like this: + +@example +find / -name xyzzy -print0 > list +xargs --null --arg-file=list munge +@end example + +The example above runs the @code{munge} program on all the files named +@file{xyzzy} that we can find, but @code{munge}'s input will still be +the terminal (or whatever the shell was using as standard input). If +your shell has the ``process substitution'' feature @samp{<(...)}, you +can do this in just one step: + +@example +xargs --null --arg-file=<(find / -name xyzzy -print0) munge +@end example + @node Limiting Command Size @subsubsection Limiting Command Size @@ -1747,7 +1767,7 @@ operation is equivalent to @samp{find -exec} (@pxref{Single File}). @item --replace@r{[}=@var{replace-str}@r{]} @itemx -i@r{[}@var{replace-str}@r{]} Replace occurrences of @var{replace-str} in the initial arguments with -names read from standard input. Also, unquoted blanks do not +names read from the input. Also, unquoted blanks do not terminate arguments; instead, the input is split at newlines only. If @var{replace-str} is omitted, it defaults to @samp{@{@}} (like for @samp{find -exec}). Implies @samp{-x} and @samp{-l 1}. As an @@ -1889,7 +1909,23 @@ less `find /usr/include -name '*.h' | xargs grep -l mode_t` @noindent You can edit those files by giving an editor name instead of a file -viewing program. +viewing program: + +@example +emacs `find /usr/include -name '*.h' | xargs grep -l mode_t` +@end example + +Because there is a limit to the length of any individual command line, +there is a limit to the number of files that can be handled in this +way. We can get around this difficulty by using xargs like this: + +@example +find /usr/include -name '*.h' | xargs grep -l mode_t > todo +xargs --arg-file=todo emacs +@end example + +Here, @code{xargs} will run @code{emacs} as many times as necessary to +visit all of the files listed in the file @file{todo}. @node Archiving @section Archiving @@ -2601,6 +2637,10 @@ if some other error occurred. @end table @table @code +@item --arg-file@r{=@var{inputfile}} +@itemx -a @r{=@var{inputfile}} +Read names from the file @var{inputfile} instead of standard input. + @item --null @itemx -0 Input filenames are terminated by a null character instead of by diff --git a/xargs/xargs.1 b/xargs/xargs.1 index 0d8f7e2..dbd4fbe 100644 --- a/xargs/xargs.1 +++ b/xargs/xargs.1 @@ -7,7 +7,8 @@ xargs \- build and execute command lines from standard input [\-n max-args] [\-s max-chars] [\-P max-procs] [\-\-null] [\-\-eof[=eof-str]] [\-\-replace[=replace-str]] [\-\-max-lines[=max-lines]] [\-\-interactive] [\-\-max-chars=max-chars] [\-\-verbose] [\-\-exit] [\-\-max-procs=max-procs] -[\-\-max-args=max-args] [\-\-no-run-if-empty] [\-\-version] [\-\-help] +[\-\-max-args=max-args] [\-\-no-run-if-empty] [\-\-arg-file=file] +[\-\-version] [\-\-help] [command [initial-arguments]] .SH DESCRIPTION This manual page @@ -42,6 +43,15 @@ will stop immediately without reading any firther input. An error message is issued on stderr when this happens. .SS OPTIONS .TP +.I "\-\-arg-file=file, \-a file" +Read items from +.I file +instead of standard input. If you use this option, stdin remains +unchanged when commands are run. Otherwise, stdin is redirected +from +.IR /dev/null . + +.TP .I "\-\-null, \-0" Input items are terminated by a null character instead of by whitespace, and the quotes and backslash are not special (every diff --git a/xargs/xargs.c b/xargs/xargs.c index 54fd03b..2479c3b 100644 --- a/xargs/xargs.c +++ b/xargs/xargs.c @@ -173,8 +173,11 @@ extern char *version_string; /* The name this program was run with. */ char *program_name; -/* Buffer for reading arguments from stdin. */ +static FILE *input_stream; + +/* Buffer for reading arguments from input. */ static char *linebuf; +static int keep_stdin = 0; /* Line number in stdin since the last command was executed. */ static int lineno = 0; @@ -260,6 +263,7 @@ static boolean query_before_executing = false; static struct option const longopts[] = { {"null", no_argument, NULL, '0'}, + {"arg-file", required_argument, NULL, 'a'}, {"eof", optional_argument, NULL, 'e'}, {"replace", optional_argument, NULL, 'i'}, {"max-lines", optional_argument, NULL, 'l'}, @@ -357,6 +361,7 @@ main (int argc, char **argv) int optc; int show_limits = 0; /* --show-limits */ int always_run_command = 1; + char *input_file = "-"; /* "-" is stdin */ long posix_arg_size_max; long posix_arg_size_min; long arg_size; @@ -405,7 +410,7 @@ main (int argc, char **argv) - while ((optc = getopt_long (argc, argv, "+0e::i::l::n:prs:txP:", + while ((optc = getopt_long (argc, argv, "+0a:e::i::l::n:prs:txP:", longopts, (int *) 0)) != -1) { switch (optc) @@ -494,6 +499,10 @@ main (int argc, char **argv) proc_max = parse_num (optarg, 'P', 0L, -1L, 1); break; + case 'a': + input_file = optarg; + break; + case 'v': printf (_("GNU xargs version %s\n"), version_string); return 0; @@ -504,6 +513,22 @@ main (int argc, char **argv) } } + if (0 == strcmp (input_file, "-")) + { + input_stream = stdin; + } + else + { + keep_stdin = 1; /* see prep_child_for_exec() */ + input_stream = fopen (input_file, "r"); + if (NULL == input_stream) + { + error (1, errno, + _("Cannot open input file `%s'"), + input_file); + } + } + if (replace_pat || lines_per_exec) exit_if_size_exceeded = true; @@ -654,7 +679,7 @@ append_char_to_buf(char **pbuf, char **pend, char **pp, int c) #endif -/* Read a line of arguments from stdin and add them to the list of +/* Read a line of arguments from the input and add them to the list of arguments to pass to the command. Ignore blank lines and initial blanks. Single and double quotes and backslashes quote metacharacters and blanks as they do in the shell. @@ -681,7 +706,7 @@ read_line (void) while (1) { prevc = c; - c = getc (stdin); + c = getc (input_stream); if (c == EOF) { /* COMPAT: SYSV seems to ignore stuff on a line that @@ -782,7 +807,7 @@ read_line (void) } } -/* Read a null-terminated string from stdin and add it to the list of +/* Read a null-terminated string from the input and add it to the list of arguments to pass to the command. Return -1 if eof (either physical or logical) is reached, otherwise the length of the string read (including the null). */ @@ -800,7 +825,7 @@ read_string (void) return -1; while (1) { - int c = getc (stdin); + int c = getc (input_stream); if (c == EOF) { eof = true; @@ -1011,18 +1036,21 @@ print_args (boolean ask) static void prep_child_for_exec (void) { - const char inputfile[] = "/dev/null"; - /* fprintf(stderr, "attaching stdin to /dev/null\n"); */ - - close(0); - if (open(inputfile, O_RDONLY) < 0) + if (!keep_stdin) { - /* This is not entirely fatal, since - * executing the child with a closed - * stdin is almost as good as executing it - * with its stdin attached to /dev/null. - */ - error (0, errno, "%s", inputfile); + const char inputfile[] = "/dev/null"; + /* fprintf(stderr, "attaching stdin to /dev/null\n"); */ + + close(0); + if (open(inputfile, O_RDONLY) < 0) + { + /* This is not entirely fatal, since + * executing the child with a closed + * stdin is almost as good as executing it + * with its stdin attached to /dev/null. + */ + error (0, errno, "%s", inputfile); + } } } @@ -1214,8 +1242,8 @@ Usage: %s [-0prtx] [-e[eof-str]] [-i[replace-str]] [-l[max-lines]]\n\ [-n max-args] [-s max-chars] [-P max-procs] [--null] [--eof[=eof-str]]\n\ [--replace[=replace-str]] [--max-lines[=max-lines]] [--interactive]\n\ [--max-chars=max-chars] [--verbose] [--exit] [--max-procs=max-procs]\n\ - [--max-args=max-args] [--no-run-if-empty] [--version] [--help]\n\ - [command [initial-arguments]]\n"), + [--max-args=max-args] [--no-run-if-empty] [--arg-file=file]\n\ + [--version] [--help] [command [initial-arguments]]\n"), program_name); fputs (_("\nReport bugs to .\n"), stream); } -- 2.11.4.GIT