Hello, There are various program, especially the one written with GNU grep in mind, that expects various flags that grep in base doesn't have. While some of the flags (like --color) can be easily worked out (i.e. by patching/customising these programs) one thing that it isn't easily workable is --null, because it alters the way grep outputs its data.
--null makes grep output an ASCII NUL byte after the file name, so a program can parse the output of grep unambiguously even when the file names contains "funny" characters, such as a newline. GNU grep isn't the only one with a --null flag, also FreeBSD and NetBSD grep do (at least by looking at their manpages), so it's somewhat widespread. I searched the archives on marc.info but I haven't seen a previous discussion about this. The following patch was tried against GNU grep (installed from packages) and seem to behave consistently. I used the same text of the FreeBSD/NetBSD manpage for the description of the --null option, but I really dislike it: it feels way to verbose for what it's trying to say, but I wasn't able to come up with something better. I'm not familiar at all with the grep codebase, so I hope I'm not missing something. If this has some chances of being accepted, I guess I should also add some regress test; I'll try to work on them soon, but in the meantime I'm sending this. Thanks, Omar Polo diff e992327fc31d0277a6f8518613a7db1b9face78b /home/op/w/openbsd-src blob - 5cc228df222c54a0553f289b5da8bbbe6afd171e file + usr.bin/grep/grep.1 --- usr.bin/grep/grep.1 +++ usr.bin/grep/grep.1 @@ -49,6 +49,7 @@ .Op Fl -context Ns Op = Ns Ar num .Op Fl -label Ns = Ns Ar name .Op Fl -line-buffered +.Op Fl -null .Op Ar pattern .Op Ar .Ek @@ -297,6 +298,25 @@ instead of the filename before lines. Force output to be line buffered. By default, output is line buffered when standard output is a terminal and block buffered otherwise. +.It Fl -null +Output a zero byte (the ASCII NUL character) instead of the character +that normally follows a file name. +For example, +.Nm Fl l Fl -null +outputs a zero byte after each file name instead of the usual newline. +This option makes the output unambiguous, even in the presence of file +names containing unusual characters like newlines. +This option can be used with commands like +.Xr find 1 +.Fl print0 Ns , +.Xr perl 1 +.Fl 0 Ns , +.Xr sort 1 +.Fl z Ns , and +.Xr args 1 +.Fl 0 +to process arbitrary file names, even those that contain newline +characters. .El .Sh EXIT STATUS The blob - f41b5e20ca68c9e9a36d2f7dd3c44329c621f29b file + usr.bin/grep/grep.c --- usr.bin/grep/grep.c +++ usr.bin/grep/grep.c @@ -80,6 +80,7 @@ int vflag; /* -v: only show non-matching lines */ int wflag; /* -w: pattern must start and end on word boundaries */ int xflag; /* -x: pattern must match entire line */ int lbflag; /* --line-buffered */ +int nullflag; /* --null */ const char *labelname; /* --label=name */ int binbehave = BIN_FILE_BIN; @@ -89,6 +90,7 @@ enum { HELP_OPT, MMAP_OPT, LINEBUF_OPT, + NULL_OPT, LABEL_OPT, }; @@ -134,6 +136,7 @@ static const struct option long_options[] = {"mmap", no_argument, NULL, MMAP_OPT}, {"label", required_argument, NULL, LABEL_OPT}, {"line-buffered", no_argument, NULL, LINEBUF_OPT}, + {"null", no_argument, NULL, NULL_OPT}, {"after-context", required_argument, NULL, 'A'}, {"before-context", required_argument, NULL, 'B'}, {"context", optional_argument, NULL, 'C'}, @@ -436,6 +439,9 @@ main(int argc, char *argv[]) case LINEBUF_OPT: lbflag = 1; break; + case NULL_OPT: + nullflag = 1; + break; case HELP_OPT: default: usage(); blob - b3d24ae662beb72c5632190c5c819bcc92f0389a file + usr.bin/grep/grep.h --- usr.bin/grep/grep.h +++ usr.bin/grep/grep.h @@ -68,7 +68,7 @@ extern int cflags, eflags; extern int Aflag, Bflag, Eflag, Fflag, Hflag, Lflag, Rflag, Zflag, bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, qflag, - sflag, vflag, wflag, xflag; + sflag, vflag, wflag, xflag, nullflag; extern int binbehave; extern const char *labelname; blob - e16d08e7d859609c2ccbc0ac4bba670188f81abf file + usr.bin/grep/util.c --- usr.bin/grep/util.c +++ usr.bin/grep/util.c @@ -172,13 +172,13 @@ procfile(char *fn) if (cflag) { if (!hflag) - printf("%s:", ln.file); + printf("%s%c", ln.file, nullflag ? '\0' : ':'); printf("%llu%s\n", c, overflow ? "+" : ""); } if (lflag && c != 0) - printf("%s\n", fn); + printf("%s%c", fn, nullflag ? '\0' : '\n'); if (Lflag && c == 0) - printf("%s\n", fn); + printf("%s%c", fn, nullflag ? '\0' : '\n'); if (c && !cflag && !lflag && !Lflag && binbehave == BIN_FILE_BIN && nottext && !qflag) printf("Binary file %s matches\n", fn); @@ -266,9 +266,9 @@ print: if (Bflag > 0) printqueue(); linesqueued = 0; - printline(l, ':', oflag ? &pmatch : NULL); + printline(l, nullflag ? '\0' : ':', oflag ? &pmatch : NULL); } else { - printline(l, '-', oflag ? &pmatch : NULL); + printline(l, nullflag ? '\0' : '-', oflag ? &pmatch : NULL); tail--; } } @@ -660,7 +660,7 @@ printline(str_t *line, int sep, regmatch_t *pmatch) } if (nflag) { if (n) - putchar(sep); + putchar(nullflag ? '\0' : sep); printf("%lld", line->line_no); ++n; }