Shell – Why does grep treat ‘[D]ebug’ string differently

grepregular expressionshellwildcards

I've created simple text file named "T" to test the unusual behavior of grep:

    1 Debug
    2 debug
    3 determined
    4 Determined

Tried different syntax:

    $ grep De T
        1 Debug
        4 Determined
    $ grep de T
        2 debug
        3 determined
    $ grep Determined T
        4 Determined
    $ grep determined T
        3 determined
    $ grep Debug T
        1 Debug
    $ grep debug T
        2 debug
    $ grep [D]ebug T          # Why result is 2-nd line???
        2 debug
    $ grep [Dd]ebug T         # Why result is only one 2-nd line???
        2 debug
    $ grep [Dd]e T
        1 Debug
        2 debug
        3 determined
        4 Determined
    $ grep [d]e T
        2 debug
        3 determined
    $ grep [d]ebug T
        2 debug
    $ grep "[D]ebug" T
        1 Debug
    $ grep "[Dd]ebug" T
        1 Debug
        2 debug
    $ grep [\D]ebug T         # Why result is 2-nd line???
        2 debug
    $ grep --version
        grep (GNU grep) 2.16

As you can see, almost every grep call returns correct result, but $ grep [D]ebug T, $ grep [Dd]ebug T, $ grep [\D]ebug T return wrong results. Why is this happening?

Best Answer

I'm guessing that you probably have a file or directory called debug in your current working directory:

 $ ls -l
total 8
-rw-r--r--   1 jay   wheel   58 Feb  1 05:01 T
 $ grep [D]ebug T
    1 Debug
 $ grep [Dd]ebug T
    1 Debug
    2 debug

 $ touch debug
 $ ls -l
total 8
-rw-r--r--  1 jay  wheel  58 Feb  1 05:01 T
-rw-r--r--  1 jay  wheel   0 Feb  1 05:05 debug

 $ grep [D]ebug T
    2 debug
 $ grep [Dd]ebug T
    2 debug

I commend you on this excellent illustration why you must always escape shell metacharacters.

Update to clarify what's going on: I'm assuming you, like I, are using an OS with a case-insensitive filesystem (e.g. a Mac). When you execute the command, your shell performs a number of expansions on it before actually executing grep. One of those is filename expansion in which square brackets provide alternations:

[Dd]ebug on a case-sensitive filesystem would expand to either Debug debug, Debug or debug depending on what files it could match. Since it only matched the file debug, your command became:

grep debug T

If you remove that file and do touch Debug, you'll see the output of your command change because it will be interpolated as

grep Debug T

Without the debug file in your directory, your shell tries to interpolate the brackets but fails without a match so it happens to pass the argument through unchanged.