summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2016-05-01 22:56:39 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2016-05-01 22:56:39 -0700
commitaf6af288eac28951b5eee1eaaf373e22b2193b7b (patch)
tree41dcd89cb183bbf314984f41b676755cc59047a2
parentd1060aa9f7d1daca37656a35751241ddce67ae75 (diff)
downloadgrep-af6af288eac28951b5eee1eaaf373e22b2193b7b.tar.gz
grep: /dev/null output speedup
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.
-rw-r--r--NEWS2
-rw-r--r--src/grep.c67
-rw-r--r--tests/Makefile.am1
-rwxr-xr-xtests/grep-dev-null-out11
-rwxr-xr-xtests/status4
5 files changed, 55 insertions, 30 deletions
diff --git a/NEWS b/NEWS
index a0cae2f6..5a2225a8 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,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 8baca5a0..d812baea 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 45908ce2..7effa571 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 00000000..f30700dd
--- /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 9de98dff..2a2d6e04 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