1

I have a file with the below contents:

sh-4.2$ cat file1
example of multiple
pattern
this is an
example of multipole
sorry multiple
pattern matching
using grep
so the example is the
file itself
-example
-multiple
-bye
tata
!

While searching for "-example" in the above file, the grep command is not giving the desired output. I know if the pattern contains '-' then -e option should be used:

In the first example I used -example directly without any quotes:

sh-4.2$ grep -example file1
example of multiple
example of multipole
so the example is the
-example

-example with single quotes:

sh-4.2$ grep '-example' file1
example of multiple
example of multipole
so the example is the
-example

-example with double quotes and escape characters

sh-4.2$ grep "\-example" file1
-example
sh-4.2$

3 Answers3

3

Well, you know that the search pattern contains a '-', and you know that when the search pattern contains a '-' you need to use the -e flag. Since you're not using the -e flag the shell is interpreting your "pattern" as an argument (and a parameter) instead. You can see that with:

$ grep "-foo" file1
grep: oo: No such file or directory

By extension, your code grep "-example" file1 is telling the shell that you want to run grep with the -e argument and a parameter "xample".

This is the same problem we run into when we try something like rm -my-silly-file-name - it won't work, and we need to use something like rm ./-my-silly-file-name instead. Another workaround would be rm -- -my-silly-file-name. We can use that idiom here:

$ grep -- "-example" < file1
-example

The "--" tells the shell that everything after it is not an argument.

Alternatively, you can simply escape the "-" with a "\", as you've seen:

grep "\-example" file1

This article goes into some details about quoting: the relevant part is Parameters and backquoted commands that should be interpreted by the shell are enclosed in double quotes. When you use double-quotes, the contents are interpreted by the shell.

John N
  • 1,951
2

You "know if the pattern [begins with] '-' then -e option should be used" but you are not using it when you should. grep -e -example file1 will give you the expected result.

Here is what is actually executed in each of your examples:

  • grep -example file1 => grep -e xample file1 (-e is necessary)
  • grep '-example' file1 => grep -e xample file1 (-e is necessary)
  • grep "\-example" file1 => grep \-example file1 (-e is not necessary)
xhienne
  • 17,793
  • 2
  • 53
  • 69
2

An argument that starts with - is taken as an option.

In your first two cases, the first argument passed to grep is -example, which grep understands as -e xample (xample argument to the -e option, so search for xample).

In the third case \-example is passed to grep. As it doesn't start with -, it's not taken as an option. It's taken as a regexp instead. However the behaviour for \- is unspecified as per POSIX, so you have no guarantee as to what it matches. With most grep implementations, \- will match a -, however you can imagine grep implementations where \- may be a special operator (see for instance GNU grep where \+ is a special operator and doesn't match a literal +).

If you want -example not to be taken as an option, you need either:

  • grep -- -example file. The -- marks the end of options. Anything after it is a non-option (so the first argument the pattern to search and the rest the list of files to look in).
  • grep -e -example file. Here, -example is taken as the argument to the -e option.

That's the kind of reason why you should get used to writing:

grep -e "$pattern" file

Or

grep -- "$pattern" file

if you can't guarantee $pattern won't start with -.

Note that here, you could also write it:

grep '[-]example' file

Though see Bracket expression (without ranges) matching unexpected character in bash about possible complications with that approach.