grep getting confused by filenames with dashes

As an addition to Romeo's answer, note that

grep pattern --whatever

is required by POSIX to look for pattern in the --whatever file. That's because no options should be recognised after non-option arguments (here pattern).

GNU grep in that instance is not POSIX compliant. It can be made compliant by passing the POSIXLY_CORRECT environment variable (with any value) into its environment.

That's the case of most GNU utilities and utilities using a GNU or compatible implementation of getopt()/getopt_long() to parse command-line arguments.

There are obvious exceptions like env, where env VAR=x grep --version gets you the version of grep, not env. Another notable exception is the GNU shell (bash) where neither the interpreter nor any of its builtins accept options after non-option arguments. Even its getopts cannot parse options the GNU way.

Anyway, POSIXLY_CORRECT won't save you if you do

grep -e pattern *.js

(there, pattern is not a non-option argument, it is passed as an argument to the -e option, so more options are allowed after that).

So it's always a good idea to mark the end of options with -- when you can't guarantee that what comes after won't start with a - (or + with some tools):

grep -e pattern -- *.js
grep -- pattern *.js

or use:

grep -e pattern ./*.js

(note that grep -- pattern * won't help you if there's a file called -, while grep pattern ./* would work. grep -e "$pattern" should be used instead of grep "$pattern" in case $pattern itself may start with -).

There was an attempt in the mid-90s to have bash be able to tell getopt() which arguments (typically the ones resulting from a glob expansion) were not to be treated as options (via a _<pid>_GNU_nonoption_argv_flags_ environment variable), but that was removed as it was causing more problems than it solved.


You can try the option of grep which tell "end of parameters"

grep -- <string> <filename>

This will inform grep to ignore next dashes as parameters and thread them as next elements in command line


Just to post a TLDR answer with the other obvious fix,

grep -e pattern ./*.js

This works for commands which don't feature the -- option; though it is rather broadly supported, not all commands handle it.

(Notice also the -e which similarly works to disambiguate a pattern which starts with a dash. You can of course also work around that with something like [-]pattern but that won't work with grep -F; and if the pattern is user-supplied, it's just better not to mess with it.)

After "(almost) always quote your variables," this is probably the second most common gotcha in shell programming - wildcard matches and user-supplied data can and will occasionally contain a leading dash, so you'd better be prepared.