grep getting confused by filenames with dashes

I’m having a problem where grep gets confused when the directory contains a file starting with dashes.

For example, I have a file named “------.js” . When I do something like grep somestring * I get the error:

grep: unrecognized option '------.js'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.

This seems like the kind of question that would be asked all over the internet, but I can’t find anything.

I can manually resolve the problem with something like
find . | while read f; do grep MYSTRING "$f"; done
but I’m wondering if there’s a simpler / more robust solution.

I’m running Arch Linux.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

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

Method 3

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.

Method 4

You solution with find,

find . | while read f; do grep MYSTRING "$f"; done

may be improved:

find . -maxdepth 1 -type f -exec grep -H "MYSTRING" {} +

There is nothing “not robust” about this.

It solves the issues with the dashes in the filename since find will invoke grep with the filename prepended with the path ./.

The other two solutions are to separate the options from the filename using -- on the command line (which will indicate to the command line parsing routine that there are no more options to parse), or to specify the absolute or relative path to the file.

Method 5

find . -name '-*' -exec rename 's/[- ]//' {} ;


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x