14

I'm trying to recursively search a string with grep but I get this:

$ grep -r "stuff" *
grep: unrecognized option '---corporate-discount.csv'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.

How can I prevent Bash from passing files starting with - as argument?

  • 4
  • 3
    You don't really want to prevent the shell from passing these files, do you? The question is rather how to tell grep that they aren't options. – DonHolgo May 17 at 11:57
  • 10
    ...to be clear, bash doesn't control which results are treated as options vs treated as arguments; that's in the receiving program's control. You'd get the same behavior with, say, subprocess.Popen(['grep', '-r', '-e' 'stuff', '--corporate-discount.csv']) in Python, no bash anywhere. – Charles Duffy May 17 at 15:37
  • 1
    Related reading: Unix Wildcards Gone Wild, about security issues that can be caused by using * in commands. ALL of these can be avoided by using ./* instead. – Wildcard May 17 at 19:57
  • 1
    @Wildcard, using -- as an end-of-options sigil is perfectly reasonable as well; POSIX utility syntax guidelines require it to be honored; see guideline #10. (Sure, not all programs follow POSIX guidelines, but the answer is to string up the offending programs' authors and/or eject them from the industry). – Charles Duffy May 18 at 17:27
43

First, note that the interpretation of arguments starting with dashes is up to the program being started, grep or other. The shell has no direct way to control it.

Assuming you want to process such files (and not ignore them completely), grep, along with most programs, recognizes -- as indicating the end of options, so

grep -r -e "stuff" -- *

will do what you want. The -e is there in case stuff starts with a - as well.

Alternatively, you can also use:

grep -r -e "stuff"  ./*

That latter one would also avoid the problem if there was a file called - in the current directory. Even after the -- separator, grep interprets - as meaning stdin, while ./- is the file called - in the current directory.

8

To prevent Bash expansion from passing files starting with “-” you can use:

echo [!-]*

Which works portably in most shells, or, specific to ksh, bash, zsh:

echo !(-*)

For example: in a directory with this files

$ echo *
a b c ---corporate-discount.csv d -e --option.txt

Will list only (provided extglob is active):

$ shopt -s extglob
$ echo !(-*)
a b c d

$ echo [!-]*
a b c d

But if what you want is to process all files while telling grep to avoid interpreting files stated with a - as options, then just add a ./:

grep -r "stuff" ./*

Or, if there is guarantee that no file called exactly - exists in the files listed (grep will interpret a lonely - as read from stdin), you can use:

grep -r -- "stuff" *
  • Yes, as the question is about bash, a GNU shell, it seems reasonable to assume that a GNU grep is available, could be installed, or is actually being used. @StéphaneChazelas – Isaac May 18 at 15:20
  • Yes, a grep -r -- stuff * is simpler, and works with non-GNUish greps also. So: added, thanks. @StéphaneChazelas – Isaac May 18 at 15:27
  • @Isaac I wouldn't say it's a reasonable assumption "if bash is available, GNU grep is also available". Take for instance FreeBSD: bash isn't installed by default , which can be installed later, but there's no effect on grep - it remains BSD version of grep unless GNU grep is explicitly installed. But that's a minor nitpick. I like the alternative approach via extglob, hence +1'ed the answer – Sergiy Kolodyazhnyy May 19 at 3:18
  • 2
    @SergiyKolodyazhnyy, AFAIK, grep on FreeBSD is still based on GNU grep and still has that misfeature whereby options are recognised after non-options. Even BSDs like OpenBSD that have rewritten their grep made them GNU compatible for backward portability (and still show that behaviour here). On macOS, sh is bash, but I'd expect their grep not to show that behaviour as macOS is meant to be POSIX compliant even without $POSIXLY_CORRECT. In any case, the OP's grep is GNU-compatible since it gives that error. – Stéphane Chazelas May 19 at 6:16
  • 1
    See also echo [!-]* as a standard equivalent of ksh's (or bash -O extglob's) echo !(-*). – Stéphane Chazelas May 19 at 8:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.