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.