From 40cd25147b4461979c0d992299f2c101f9034f7a Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Tue, 6 Jun 2017 08:19:29 +0200 Subject: [PATCH 101/224] xargs: add -o, --open-tty option This option is available in the xargs implementation of FreeBSD, NetBSD, OpenBSD and in the Apple variant. Add it for compatibility. * xargs/xargs.c (open_tty): Add static flag for the new option. (longopts): Add member. (main): Handle the 'o' case in the getopt_long() loop. (prep_child_for_exec): Redirect stdin of the child to /dev/tty when the -o option is given. Furthermore, move the just-opened file descriptor to STDIN_FILENO. (usage): Document the new option. * bootstrap.conf (gnulib_modules): Add dup2. * xargs/xargs.1 (SYNOPSIS): Replace the too-long list of options by "[options]" - they are listed later anyway. (OPTIONS): Document the new option. (STANDARDS CONFORMANCE): Mention that the -o option is an extension. * doc/find.texi (xargs options): Document the new option. (Invoking the shell from xargs): Amend the explanation of the redirection example with a note about the -o option. (Viewing And Editing): Likewise. (Error Messages From xargs): Add the message when dup2() fails. (NEWS): Mention the new option. Fixes http://savannah.gnu.org/bugs/?51151 --- NEWS | 4 ++++ bootstrap.conf | 1 + doc/find.texi | 24 ++++++++++++++++++++++++ xargs/xargs.1 | 41 +++++++++++++---------------------------- xargs/xargs.c | 33 ++++++++++++++++++++++++++------- 5 files changed, 68 insertions(+), 35 deletions(-) diff --git a/doc/find.texi b/doc/find.texi index 24d8ef99..4516b64c 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -3746,6 +3746,17 @@ Use at most @var{max-args} arguments per command line. Fewer than option) is exceeded, unless the @samp{-x} option is given, in which case @code{xargs} will exit. +@item --open-tty +@itemx -o +Reopen stdin as @file{/dev/tty} in the child process before executing +the command, thus allowing that command to be associated to the terminal +while @code{xargs} reads from a different stream, e.g. from a pipe. +This is useful if you want @code{xargs} to run an interactive application. +@example +grep -lz PATTERN * | xargs -0o vi +@end example + + @item --interactive @itemx -p Prompt the user about whether to run each command line and read a line @@ -3877,6 +3888,10 @@ Therefore instead of keeping a @code{sh} process around for no reason, we just arrange for the subshell to exec Emacs, saving an extra process creation. +Although GNU @code{xargs} and the implementations on some other platforms +like BSD support the @samp{-o} option to achieve the same, the above is +the portable way to redirect stdin to @file{/dev/tty}. + Sometimes, though, it can be helpful to keep the shell process around: @example @@ -4050,6 +4065,10 @@ protect the @code{$@@} against expansion by your interactive shell nothing). The capitalised @samp{Emacs} on the command line is used as @code{$0} by the shell that @code{xargs} launches. +Please note that the implementations in GNU @code{xargs} and at least BSD +support the @samp{-o} option as extension to achieve the same, while the +above is the portable way to redirect stdin to @file{/dev/tty}. + @node Archiving @section Archiving @@ -5564,6 +5583,11 @@ signals to increase or decrease the parallelism of its processing. If you don't plan to send it those signals, this warning can be ignored (though if you're a programmer, you may want to help us figure out why @code{xargs} is confused by your operating system). + +@item failed to redirect standard input of the child process +@code{xargs} redirects the stdin stream of the command to be run to either +@file{/dev/null} or to @file{/dev/tty} for the @samp{-o} option. +See the manual of the system call @code{dup2(2)}. @end table @node Error Messages From locate diff --git a/xargs/xargs.1 b/xargs/xargs.1 index 68377868..f66dbc05 100644 --- a/xargs/xargs.1 +++ b/xargs/xargs.1 @@ -4,34 +4,7 @@ xargs \- build and execute command lines from standard input .SH SYNOPSIS .B xargs .nh -[\fB\-0prtx\fR] -[\fB\-E \fIeof-str\fR] -[\fB\-e\fR[\fIeof-str\fR]] -[\fB\-\-eof\fR[=\fIeof-str\fR]] -[\fB\-\-null\fR] -[\fB\-d \fIdelimiter\fR] -[\fB\-\-delimiter \fIdelimiter\fR] -[\fB\-I \fIreplace-str\fR] -[\fB\-i\fR[\fIreplace-str\fR]] -[\fB\-\-replace\fR[=\fIreplace-str\fR]] -[\fB\-l\fR[\fImax-lines\fR]] -[\fB\-L \fImax-lines\fR] -[\fB\-\-max\-lines\fR[=\fImax-lines\fR]] -[\fB\-n \fImax-args\fR] -[\fB\-\-max\-args\fR=\fImax-args\fR] -[\fB\-s \fImax-chars\fR] -[\fB\-\-max\-chars\fR=\fImax-chars\fR] -[\fB\-P \fImax-procs\fR] -[\fB\-\-max\-procs\fR=\fImax-procs\fR] -[\fB\-\-process\-slot\-var\fR=\fIname\fR] -[\fB\-\-interactive\fR] -[\fB\-\-verbose\fR] -[\fB\-\-exit\fR] -[\fB\-\-no\-run\-if\-empty\fR] -[\fB\-\-arg\-file\fR=\fIfile\fR] -[\fB\-\-show\-limits\fR] -[\fB\-\-version\fR] -[\fB\-\-help\fR] +[\fIoptions\fR] [\fIcommand\fR [\fIinitial-arguments\fR]] .hy .SH DESCRIPTION @@ -252,6 +225,15 @@ arrange for each process to produce a separate output file (or otherwise use separate resources). .TP .PD +.B \-o, \-\-open\-tty +Reopen stdin as +.I /dev/tty +in the child process before executing the command. This is useful if +you want +.B xargs +to run an interactive application. +.TP +.PD .B \-p, \-\-interactive Prompt the user about whether to run each command line and read a line from the terminal. Only run the command line if the response starts @@ -404,6 +386,9 @@ The \-l and \-i options appear in the 1997 version of the POSIX standard, but do not appear in the 2004 version of the standard. Therefore you should use \-L and \-I instead, respectively. .P +The \-o option is an extension to the POSIX standard for better +compatibility with BSD. +.P The POSIX standard allows implementations to have a limit on the size of arguments to the .B exec diff --git a/xargs/xargs.c b/xargs/xargs.c index 5cf8c131..a2917e48 100644 --- a/xargs/xargs.c +++ b/xargs/xargs.c @@ -151,6 +151,9 @@ static volatile int child_error = EXIT_SUCCESS; static volatile int original_exit_value; +/* If true, open /dev/tty in the child process before executing the command. */ +static bool open_tty = false; /* option -o */ + /* If true, print each command on stderr before executing it. */ static bool print_command = false; /* Option -t */ @@ -185,6 +188,7 @@ static struct option const longopts[] = {"replace", optional_argument, NULL, 'I'}, {"max-lines", optional_argument, NULL, 'l'}, {"max-args", required_argument, NULL, 'n'}, + {"open-tty", no_argument, NULL, 'o'}, {"interactive", no_argument, NULL, 'p'}, {"no-run-if-empty", no_argument, NULL, 'r'}, {"max-chars", required_argument, NULL, 's'}, @@ -509,7 +513,7 @@ main (int argc, char **argv) bc_use_sensible_arg_max (&bc_ctl); } - while ((optc = getopt_long (argc, argv, "+0a:E:e::i::I:l::L:n:prs:txP:d:", + while ((optc = getopt_long (argc, argv, "+0a:E:e::i::I:l::L:n:oprs:txP:d:", longopts, &option_index)) != -1) { switch (optc) @@ -608,6 +612,10 @@ main (int argc, char **argv) bc_ctl.exit_if_size_exceeded = true; break; + case 'o': + open_tty = true; + break; + case 'p': query_before_executing = true; print_command = true; @@ -1185,22 +1193,30 @@ prep_child_for_exec (void) unsigned int slot = add_proc (0); set_slot_var (slot); - if (!keep_stdin) + if (!keep_stdin || open_tty) { - const char inputfile[] = "/dev/null"; - /* fprintf (stderr, "attaching stdin to /dev/null\n"); */ + int fd; + const char *inputfile = open_tty ? "/dev/tty" : "/dev/null"; close (0); - if (open (inputfile, O_RDONLY) < 0) + if ((fd = open (inputfile, O_RDONLY)) < 0) { - /* This is not entirely fatal, since + /* Treat a failure to open /dev/tty as fatal. + * The other case 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", + error (open_tty ? EXIT_FAILURE : 0, errno, "%s", quotearg_n_style (0, locale_quoting_style, inputfile)); } + if (STDIN_FILENO < fd) + { + if (dup2(fd, STDIN_FILENO) != 0) + error (EXIT_FAILURE, errno, + _("failed to redirect standard input of the child process")); + close(fd); + } } } @@ -1676,6 +1692,9 @@ usage (int status) HTL (_(" -l[MAX-LINES] similar to -L but defaults to at most one non-\n" " blank input line if MAX-LINES is not specified\n")); HTL (_(" -n, --max-args=MAX-ARGS use at most MAX-ARGS arguments per command line\n")); + HTL (_(" -o, --open-tty Reopen stdin as /dev/tty in the child process\n" + " before executing the command; useful to run an\n" + " interactive application.\n")); HTL (_(" -P, --max-procs=MAX-PROCS run at most MAX-PROCS processes at a time\n")); HTL (_(" -p, --interactive prompt before running commands\n")); HTL (_(" --process-slot-var=VAR set environment variable VAR in child processes\n")); -- 2.19.1