Hello everyone, one year ago there was a thread for grep --null [0] and there seemed to be a general agreement (in the diff, not only in how wrong the name --null feels.) In the end it wasn't committed, I got distracted and continued to happily using my patched ~/bin/grep
[0]: https://marc.info/?l=openbsd-tech&m=160975008513761&w=2 The attached diff was tweaked by benno@ (also after some comments from tedu@) and now i've just fixed an issue in printline which reminded me that this is still pending. (we need to print the NUL byte after the filename and skip the next separator if we want to follow the GNU grep behavior: freebsd' grep does this, netbsd by glancing at the code doesn't.) One thing that wasn't clear in the other thread is the choice of the flag: GNU grep has "-Z, --null" and "-z, --null-data" while our grep has -Z to force it to behave as zgrep and no -z. We also can't use -0 because it stands for "print 0 lines of context" (both in GNU grep and ours grep); while it's unlikely that someone is relying on it, it currently "works" and dumping NULs into some pipeline not prepared for them is not sensible. (e.g. a script that does `| grep -$n'...) ok? diff 8f97b6e548e6b59d6e9019eeb9ee638e555c44c2 4cccbdfb2bec5951dcf534df8036beb8a0f85271 blob - 5cc228df222c54a0553f289b5da8bbbe6afd171e blob + e1edae7e432a155ddf71433717f58c8017744386 --- 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,16 @@ 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 instead of the character that normally follows a +file name. +This option makes the output unambiguous, even in the presence of file +names containing unusual characters like newlines, making the output +suitable for use with the +.Fl 0 +option to +.Xr xargs 1 . +This option is a non-POSIX extension and may not be portable. .El .Sh EXIT STATUS The blob - f41b5e20ca68c9e9a36d2f7dd3c44329c621f29b blob + 279d949fae764262a350e66ce1c21a6864284eb6 --- 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 - 731bbcc35af4cbf870aaf70ce9913e375142d4d8 blob + 16a1e94a0bcf58498dd4df8d6c7692f1ffdb8045 --- 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 - bcef0897881eb7890ab3e568a92a77ae5fe1f4a7 blob + 39bf78b02d561c7cbb8c518300cc725aa373fb71 --- 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); @@ -651,27 +651,32 @@ grep_revstr(unsigned char *str, int len) void printline(str_t *line, int sep, regmatch_t *pmatch) { - int n; + int n, printsep; n = 0; + printsep = !nullflag; if (!hflag) { fputs(line->file, stdout); + if (nullflag) + putchar(0); ++n; } if (nflag) { - if (n) + if (n && printsep) putchar(sep); printf("%lld", line->line_no); + printsep = 1; ++n; } if (bflag) { - if (n) + if (n && printsep) putchar(sep); printf("%lld", (long long)line->off + (pmatch ? pmatch->rm_so : 0)); + printsep = 1; ++n; } - if (n) + if (n && printsep) putchar(sep); if (pmatch) fwrite(line->dat + pmatch->rm_so,
