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?

terdon
  • 234,489
  • 66
  • 447
  • 667
Chef Tony
  • 455
  • 4
  • 9
  • 5
    Related: [How do I delete a file whose name begins with “-” (hyphen a.k.a. dash or minus)?](https://unix.stackexchange.com/questions/1519/how-do-i-delete-a-file-whose-name-begins-with-hyphen-a-k-a-dash-or-minus) – steeldriver May 17 '19 at 10:59
  • 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 '19 at 11:57
  • 11
    ...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 '19 at 15:37
  • 2
    Related reading: [Unix Wildcards Gone Wild](https://www.defensecode.com/public/DefenseCode_Unix_WildCards_Gone_Wild.txt), about security issues that can be caused by using `*` in commands. ALL of these can be avoided by using `./*` instead. – Wildcard May 17 '19 at 19:57
  • 2
    @Wildcard, using `--` as an end-of-options sigil is perfectly reasonable as well; [POSIX utility syntax guidelines](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html) 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 '19 at 17:27

2 Answers2

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.

ilkkachu
  • 133,243
  • 15
  • 236
  • 397
icarus
  • 17,420
  • 1
  • 37
  • 54
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 –  May 18 '19 at 15:20
  • Yes, a `grep -r -- stuff *` is simpler, and works with non-GNUish greps also. So: added, thanks. @StéphaneChazelas –  May 18 '19 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](https://www.freebsd.org/doc/en_US.ISO8859-1/articles/linux-users/shells.html) , 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 '19 at 03: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 '19 at 06:16
  • @StéphaneChazelas In other words, truly POSIX-compliant grep shouldn't have problems with dash-leading filenames ? – Sergiy Kolodyazhnyy May 19 '19 at 08:15
  • @SergiyKolodyazhnyy, yes `grep foo --help` is required by POSIX to match `foo` against the lines of the `--help` file. GNU `grep` only does it when `$POSIXLY_CORRECT` is in the environment. You still need the `--` even with POSIX compliant implementations with `grep -e foo -- --help` as here `foo` is an argument to the `-e` option, it's not a non-option argument. – Stéphane Chazelas May 19 '19 at 08:27
  • 1
    See also `echo [!-]*` as a standard equivalent of ksh's (or `bash -O extglob`'s) `echo !(-*)`. – Stéphane Chazelas May 19 '19 at 08:32