This sped up 'seq 10000000000 | grep . >/dev/null' by a factor of 380,000 on my platform (Fedora 23, x86-64, AMD Phenom II X4 910e, en_US.UTF-8 locale). * NEWS: Document this. * src/grep.c (grepbuf): exit_on_match no longer implies that -q was specified, so when a match is found, exit with exit_failure if an error was also found. (grepdesc): Omit unnecessary S_ISREG and st_ino checks. out_stat.st_ino is zero if stdout is not a regular file, and this cannot possibly equal st->st_ino. (main): Omit duplicate initialization of exit_failure. Do not bother with isatty unless -q is not used and stdout is a character special file and --color=auto and TERM says colorization is possible. Most importantly, set exit_on_match if the output is /dev/null. * tests/grep-dev-null-out: New test. * tests/Makefile.am (TESTS): Add it. * tests/status: Do not require grep to actually read all the input files when the output is /dev/null and a matching line has been found. --- NEWS | 2 ++ src/grep.c | 67 ++++++++++++++++++++++++++++--------------------- tests/Makefile.am | 1 + tests/grep-dev-null-out | 11 ++++++++ tests/status | 4 +-- 5 files changed, 55 insertions(+), 30 deletions(-) create mode 100755 tests/grep-dev-null-out
diff --git a/NEWS b/NEWS index 63767aa..2af0d6d 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,8 @@ GNU grep NEWS -*- outline -*- ** Improvements + grep can be much faster now when standard output is /dev/null. + grep now outputs details more consistently when reporting a write error. E.g., "grep: write error: No space left on device" rather than just "grep: write error". diff --git a/src/grep.c b/src/grep.c index 8baca5a..d812bae 100644 --- a/src/grep.c +++ b/src/grep.c @@ -1387,7 +1387,7 @@ grepbuf (char *beg, char const *lim) if (!outleft || done_on_match) { if (exit_on_match) - exit (EXIT_SUCCESS); + exit (errseen ? exit_failure : EXIT_SUCCESS); break; } } @@ -1751,7 +1751,6 @@ grepdesc (int desc, bool command_line) input==output, while there is no risk of infloop, there is a race condition that could result in "alternate" output. */ if (!out_quiet && list_files == 0 && 1 < max_count - && S_ISREG (out_stat.st_mode) && out_stat.st_ino && SAME_INODE (st, out_stat)) { if (! suppress_errors) @@ -2280,7 +2279,6 @@ main (int argc, char **argv) textdomain (PACKAGE); #endif - exit_failure = EXIT_TROUBLE; atexit (clean_up_stdout); last_recursive = 0; @@ -2579,25 +2577,36 @@ main (int argc, char **argv) } - if (color_option == 2) - color_option = isatty (STDOUT_FILENO) && should_colorize (); - init_colorize (); + if (show_version) + { + version_etc (stdout, program_name, PACKAGE_NAME, VERSION, AUTHORS, + (char *) NULL); + return EXIT_SUCCESS; + } - /* POSIX says that -q overrides -l, which in turn overrides the - other output options. */ - if (exit_on_match) - list_files = 0; - if (exit_on_match | list_files) + if (show_help) + usage (EXIT_SUCCESS); + + bool possibly_tty = false; + struct stat tmp_stat; + if (! exit_on_match && fstat (STDOUT_FILENO, &tmp_stat) == 0) { - count_matches = false; - done_on_match = true; + if (S_ISREG (tmp_stat.st_mode)) + out_stat = tmp_stat; + else if (S_ISCHR (tmp_stat.st_mode)) + { + struct stat null_stat; + if (stat ("/dev/null", &null_stat) == 0 + && SAME_INODE (tmp_stat, null_stat)) + exit_on_match = true; + else + possibly_tty = true; + } } - out_quiet = count_matches | done_on_match; - if (out_after < 0) - out_after = default_context; - if (out_before < 0) - out_before = default_context; + if (color_option == 2) + color_option = possibly_tty && should_colorize () && isatty (STDOUT_FILENO); + init_colorize (); if (color_option) { @@ -2610,19 +2619,21 @@ main (int argc, char **argv) parse_grep_colors (); } - if (show_version) + /* POSIX says -c, -l and -q are mutually exclusive. In this + implementation, -q overrides -l and -L, which in turn override -c. */ + if (exit_on_match) + list_files = 0; + if (exit_on_match | list_files) { - version_etc (stdout, program_name, PACKAGE_NAME, VERSION, AUTHORS, - (char *) NULL); - return EXIT_SUCCESS; + count_matches = false; + done_on_match = true; } + out_quiet = count_matches | done_on_match; - if (show_help) - usage (EXIT_SUCCESS); - - struct stat tmp_stat; - if (fstat (STDOUT_FILENO, &tmp_stat) == 0 && S_ISREG (tmp_stat.st_mode)) - out_stat = tmp_stat; + if (out_after < 0) + out_after = default_context; + if (out_before < 0) + out_before = default_context; if (keys) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 45908ce..7effa57 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -82,6 +82,7 @@ TESTS = \ fmbtest \ foad1 \ grep-dev-null \ + grep-dev-null-out \ grep-dir \ help-version \ high-bit-range \ diff --git a/tests/grep-dev-null-out b/tests/grep-dev-null-out new file mode 100755 index 0000000..f30700d --- /dev/null +++ b/tests/grep-dev-null-out @@ -0,0 +1,11 @@ +#!/bin/sh +# Outputting to /dev/null. + +. "${srcdir=.}/init.sh"; path_prepend_ ../src + +require_timeout_ + +${AWK-awk} 'BEGIN {while (1) print "x"}' </dev/null | + timeout 1 grep x >/dev/null || fail=1 + +Exit $fail diff --git a/tests/status b/tests/status index 9de98df..2a2d6e0 100755 --- a/tests/status +++ b/tests/status @@ -47,9 +47,9 @@ else fail=1 fi - # should return 2 file not found + # should return 0 (found a match) or 2 (file not found) echo "abcd" | grep -E -s 'abc' - MMMMMMMM.MMM > /dev/null 2>&1 - if test $? -ne 2 ; then + if test $? -ne 0 && test $? -ne 2 ; then echo "Status: Wrong status code, test \#5 failed" fail=1 fi -- 2.5.5