diff --git a/xargs/xargs.c b/xargs/xargs.c index 7e3db67a..3a96c0e3 100644 --- a/xargs/xargs.c +++ b/xargs/xargs.c @@ -167,9 +167,22 @@ static char input_delimiter = '\0'; */ static char* slot_var_name = NULL; +/* Boolean flag whether child processed terminated by SIGPIPE shall be diagnosed + * or not. Traditional behavior is to output an error diagnostic on stderr + * when a child process was terminated by SIGPIPE. + * If TRUE, the writing of that error diagnostic will be skipped. Use case: + * seq 100 | xargs --sigpipe-nowarn COMMAND | head -n3 + * Please note that this option implicitly sets the SIGPIPE handler to SIG_DFL + * in the child process, although the invoking shell might have set it to + * SIG_IGN. + * If FALSE, i.e., when --child-sigpipe-fatal is not given, + * then we get the Traditional behavior, sigpipe enabled. */ +static bool sigpipe_nowarn_option; + enum LongOptionIdentifier { - PROCESS_SLOT_VAR = CHAR_MAX+1 + PROCESS_SLOT_VAR = CHAR_MAX+1, + SIGPIPE_NOWARN_OPTION }; static struct option const longopts[] = @@ -190,6 +203,7 @@ static struct option const longopts[] = {"exit", no_argument, NULL, 'x'}, {"max-procs", required_argument, NULL, 'P'}, {"process-slot-var", required_argument, NULL, PROCESS_SLOT_VAR}, + {"sigpipe-nowarn", no_argument, NULL, SIGPIPE_NOWARN_OPTION}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, no_argument, NULL, 0} @@ -701,6 +715,10 @@ main (int argc, char **argv) } break; + case SIGPIPE_NOWARN_OPTION: + sigpipe_nowarn_option = true; + break; + default: usage (EXIT_FAILURE); } @@ -1227,6 +1245,20 @@ prep_child_for_exec (void) unsigned int slot = add_proc (0); set_slot_var (slot); + /* For the --sigpipe-no-warn option to work, ensure that the SIGPIPE signal + * handler is set to default for the child process. When the child process + * was terminated by a signal, the parent xargs(1) will end the input + * processing and terminate itself. + * Otherwise, if SIGPIPE is ignored, then write() errors on a closed pipe + * would return with errno set to EPIPE, the child process would (hopefully) + * diagnose the error, and exit with value 1. The parent xargs(1) would not + * be able to distinguish from any other error, and not end the processing. + */ + if (sigpipe_nowarn_option) + { + signal (SIGPIPE, SIG_DFL); + } + if (!keep_stdin || open_tty) { int fd; @@ -1582,8 +1614,16 @@ wait_for_proc (bool all, unsigned int minreap) error (XARGS_EXIT_CLIENT_FATAL_SIG, 0, _("%s: stopped by signal %d"), bc_state.cmd_argv[0], WSTOPSIG (status)); if (WIFSIGNALED (status)) - error (XARGS_EXIT_CLIENT_FATAL_SIG, 0, - _("%s: terminated by signal %d"), bc_state.cmd_argv[0], WTERMSIG (status)); + { + int sig = WTERMSIG (status); + if (sigpipe_nowarn_option && sig == SIGPIPE) + { + /* Exit without the regular error diagnostic. */ + exit (XARGS_EXIT_CLIENT_FATAL_SIG); + } + error (XARGS_EXIT_CLIENT_FATAL_SIG, 0, + _("%s: terminated by signal %d"), bc_state.cmd_argv[0], sig); + } if (WEXITSTATUS (status) != 0) child_error = XARGS_EXIT_CLIENT_EXIT_NONZERO; } @@ -1744,6 +1784,8 @@ usage (int status) " if this option is not given, COMMAND will be\n" " run at least once\n")); HTL (_(" -s, --max-chars=MAX-CHARS limit length of command line to MAX-CHARS\n")); + HTL (_(" --sigpipe-nowarn suppress diagnostic if COMMAND was terminated\n" + " by the SIGPIPE signal\n")); HTL (_(" --show-limits show limits on command-line length\n")); HTL (_(" -t, --verbose print commands before executing them\n")); HTL (_(" -x, --exit exit if the size (see -s) is exceeded\n"));