Hi,

The -m, or --max-count, option allows the user to stop grep(1)
processing after a given number of matches are found.
If you use constructs such as

  $ grep foo foo.txt | head -n1

you will want to use the much faster version

  $ grep -m1 foo foo.txt

This option already exists in at least Linux, FreeBSD and NetBSD.

For example, I need grep -m for vimtex (see Issue #1018 on GitHub[0]).
But, if I had known that this exists, I would have used it in the
past with other scripts.

The following diff is adapted from FreeBSD and I tested it with vimtex
and a few hand-crafted examples. We could probably do some tricks with
mcount and mlimit, but I do not think it is worth it. OK?

[0] -- https://github.com/lervag/vimtex/issues/1018

Index: grep.c
===================================================================
RCS file: /cvs/src/usr.bin/grep/grep.c,v
retrieving revision 1.55
diff -u -p -u -p -r1.55 grep.c
--- grep.c      28 Nov 2015 01:17:12 -0000      1.55
+++ grep.c      7 Dec 2017 15:23:00 -0000
@@ -71,6 +71,9 @@ int    cflag;         /* -c: only show a count of
 int     hflag;         /* -h: don't print filename headers */
 int     iflag;         /* -i: ignore case */
 int     lflag;         /* -l: only show names of files with matches */
+int     mflag;         /* -m x: stop reading the files after x matches */
+long long mcount;      /* count for -m */
+long long mlimit;      /* requested value for -m */
 int     nflag;         /* -n: show line numbers in front of matching lines */
 int     oflag;         /* -o: print each match */
 int     qflag;         /* -q: quiet mode (don't output anything) */
@@ -107,9 +110,9 @@ usage(void)
 {
        fprintf(stderr,
 #ifdef NOZ
-           "usage: %s [-abcEFGHhIiLlnoqRsUVvwx] [-A num] [-B num] [-C[num]]\n"
+           "usage: %s [-abcEFGHhIiLlmnoqRsUVvwx] [-A num] [-B num] [-C[num]]\n"
 #else
-           "usage: %s [-abcEFGHhIiLlnoqRsUVvwxZ] [-A num] [-B num] [-C[num]]\n"
+           "usage: %s [-abcEFGHhIiLlmnoqRsUVvwxZ] [-A num] [-B num] 
[-C[num]]\n"
 #endif
            "\t[-e pattern] [-f file] [--binary-files=value] 
[--context[=num]]\n"
            "\t[--line-buffered] [pattern] [file ...]\n", __progname);
@@ -117,9 +120,9 @@ usage(void)
 }
 
 #ifdef NOZ
-static const char optstr[] = "0123456789A:B:CEFGHILRUVabce:f:hilnoqrsuvwxy";
+static const char optstr[] = "0123456789A:B:CEFGHILRUVabce:f:hilm:noqrsuvwxy";
 #else
-static const char optstr[] = "0123456789A:B:CEFGHILRUVZabce:f:hilnoqrsuvwxy";
+static const char optstr[] = "0123456789A:B:CEFGHILRUVZabce:f:hilm:noqrsuvwxy";
 #endif
 
 static const struct option long_options[] =
@@ -147,6 +150,7 @@ static const struct option long_options[
        {"ignore-case",         no_argument,            NULL, 'i'},
        {"files-without-match", no_argument,            NULL, 'L'},
        {"files-with-matches",  no_argument,            NULL, 'l'},
+       {"max-count",           required_argument,      NULL, 'm'},
        {"line-number",         no_argument,            NULL, 'n'},
        {"quiet",               no_argument,            NULL, 'q'},
        {"silent",              no_argument,            NULL, 'q'},
@@ -375,6 +379,14 @@ main(int argc, char *argv[])
                case 'l':
                        Lflag = 0;
                        lflag = qflag = 1;
+                       break;
+               case 'm':
+                       mflag = 1;
+                       errno = 0;
+                       mlimit = mcount = strtonum(optarg, 1, LLONG_MAX,
+                          &errstr);
+                       if (errstr != NULL)
+                               errx(2, "mcount %s", errstr);
                        break;
                case 'n':
                        nflag = 1;
Index: grep.h
===================================================================
RCS file: /cvs/src/usr.bin/grep/grep.h,v
retrieving revision 1.24
diff -u -p -u -p -r1.24 grep.h
--- grep.h      14 Dec 2015 20:02:07 -0000      1.24
+++ grep.h      7 Dec 2017 15:23:00 -0000
@@ -66,14 +66,17 @@ extern int   cflags, eflags;
 /* Command line flags */
 extern int      Aflag, Bflag, Eflag, Fflag, Hflag, Lflag,
                 Rflag, Zflag,
-                bflag, cflag, hflag, iflag, lflag, nflag, oflag, qflag, sflag,
-                vflag, wflag, xflag;
+                bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, qflag,
+                sflag, vflag, wflag, xflag;
 extern int      binbehave;
 
 extern int      first, matchall, patterns, tail, file_err;
 extern char    **pattern;
 extern fastgrep_t *fg_pattern;
 extern regex_t *r_pattern;
+
+/* For -m max-count */
+extern long long mcount, mlimit;
 
 /* For regex errors  */
 #define RE_ERROR_BUF 512
Index: util.c
===================================================================
RCS file: /cvs/src/usr.bin/grep/util.c,v
retrieving revision 1.57
diff -u -p -u -p -r1.57 util.c
--- util.c      3 Apr 2017 16:18:35 -0000       1.57
+++ util.c      7 Dec 2017 15:23:00 -0000
@@ -97,6 +97,8 @@ procfile(char *fn)
        file_t *f;
        int c, t, z, nottext;
 
+       mcount = mlimit;
+
        if (fn == NULL) {
                fn = "(standard input)";
                f = grep_fdopen(STDIN_FILENO, "r");
@@ -140,6 +142,8 @@ procfile(char *fn)
                        linesqueued++;
                }
                c += t;
+               if (mflag && mcount <= 0)
+                       break;
        }
        if (Bflag > 0)
                clearqueue();
@@ -223,6 +227,10 @@ redo:
 print:
        if (vflag)
                c = !c;
+
+       /* Count the matches if we have a match limit */
+       if (mflag)
+               mcount -= c;
 
        if (c && binbehave == BIN_FILE_BIN && nottext)
                return c; /* Binary file */

Reply via email to