Subject: bug#54174: (MacOS Monterey 12.2.1: zsh): grep "string" * is interpreted as grep -V when directory has a filename "-Vfilename.ext"

From: Bob Proulx <[email-masked]>
Date: Sat, 26 Feb 2022 15:53:05 -0700

close 54174
thanks

Marja Koivunen wrote:
> I had a directory with filenames that started with "-"

Of course that is okay.  However it is unusual.  And it means you need
to be extra careful in handling it such as to avoid it being confused
with an option.

> doing grep on that directory for a "string" did not find anything
> although "string was on some of the files"

You did not show us the exact command that you used.  We only know
what you show us in your email.  But in this case I will guess with
some confidence that you did something like this.

    $ grep PATTERN *      <==  Error!  Data dependent!

And that failed.  Which is not surprising since the "*" will be
expanded by your command shell to match every file in the current
working directory.  And you have just said that one of those files
(a directory is also a file) started with a '-' character.

To understand what is happening it is useful to use 'echo' here to
echo print what the command shell has done with the line and with
expanding the shell file glob characters.  (The * is called a glob
character because it matches a glob of characters.)

    $ echo grep PATTERN *

If you do that then what you see depends upon what is there.  But if
what is there is a file or a directory that starts with a dash then it
will show there as starting with a dash.  Here is a full test case.

    $ mkdir /tmp/optiontest
    $ cd /tmp/optiontest
    $ echo foo > ./--help
    $ echo grep foo *
    grep foo --help
    $ grep foo * | head
    Usage: grep [OPTION]... PATTERNS [FILE]...
    Search for PATTERNS in each FILE.
    Example: grep -i 'hello world' menu.h main.c
    PATTERNS can contain multiple patterns separated by newlines.

    Pattern selection and interpretation:
      -E, --extended-regexp     PATTERNS are extended regular expressions
      -F, --fixed-strings       PATTERNS are strings
      -G, --basic-regexp        PATTERNS are basic regular expressions
      -P, --perl-regexp         PATTERNS are Perl regular expressions

Here we see that the * was expanded by the comand shell to the matched
files.  And since a file --help was created it matched, was expanded,
was passed to grep.  The grep program saw the --help and acted.

Programs do not know that the user typed in a "*" since the command
shell expands those shell meta-characters before invoking the
program.  The program only sees the result of the expansion.

> grep just kept repeating something ... FreeBSD

Yes.  Just as explained above.

The problem is that a plain * will be expanded to match files in the
current directory.  That's why I labeled it as a data dependent
failure.  It depends upon the files that are present.  If no files
start with an option dash then it won't be seen.  If one or more files
start with an option dash then it will.  Therefore I think it is
always problematic to use a * like that.  Instead use a ./* to ensure
that the first letter is never an option dash.  This would be safer.

    $ grep PATTERN ./*

In that case using our optiontest directory the same as above.

    $ echo grep PATTERN ./*
    grep foo ./--help

That will never be confused with an option because it starts with a ./
and that is not an option start sequence.  It will never be confused
with an option.  Therefore I strongly recommend always using the file
glob form ./* instead.

> Finally, (with some help) I understood that grep interpreted
> "-Vfiename" as an option -V and gave the version info instead of
> doing grep "string" *

Correct.

> Maybe there is a way to add space after "-V " and possibly also
> other options that could be used as part of a filename in some
> operating systems?

That is not possible to do in grep.  Since the shell has already
modified the command line to expand the * by the time grep has it.
Only the command shell has that information.  And this is a feature of
the design rather than a bug.  This is all happening by the
intentional design of it.

Since this is not a bug but simply the usage of it I have marked this
bug as closed.  However further discussion is logged to the ticket.
Other people who read the ticket will see the resolution and will
learn from it.  If you have further questions or comments please
follow-up.  The ticket can always be opened again if there is an
updated reason to do so.

Bob