6

If you take this code:

echo -e '\t\t\tString' | grep '^[\t]*String'

the result is blank because it doesn't match, yet this:

echo -e '\t\t\tString' | grep $'^[\t]*String'

works. I swear that I must have used the first line's code a hundred times in my scripts and in the terminal, without ever using the "$" character like that, and it's always seemed to work. Has there been some recent change? Why does it need the "$" character? Or am I doing something wrong?

CodeGnome
  • 7,820
Tal
  • 2,112
  • As explained by @CodeGnome, "GNU sed perform their own expansion of escape characters, but GNU grep doesn't". I now realize that I had used sed previously without using $, and assumed grep worked exactly the same. Turns out it does not. – Tal Nov 16 '14 at 04:08
  • In that case, please take a minute and click the check mark under the vote count to the left of CodeGnome's answer. This will signify to everyone that your issue's been resolved. – terdon Nov 16 '14 at 04:17
  • http://stackoverflow.com/questions/1825552/grep-a-tab-in-unix – Ciro Santilli OurBigBook.com Aug 28 '16 at 11:18

3 Answers3

9

ANSI-C Quoting

According to the Bash manual, this is called ANSI-C quoting. The manual says:

Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.

In practice, this means that '\t' will not be expanded into a tab character, but $'\t' will. The output should be equivalent to using echo -e, but can be used anywhere you'd use a string without requiring command substitution.

Utilities like GNU sed perform their own expansion of escape characters, but GNU grep doesn't. The Bash shell, not grep, expands escaped characters within ANSI-C quoted strings. Without the ANSI-C quoting, the regular expression you posted contains no tab characters to match the input.

CodeGnome
  • 7,820
  • Utilities like GNU sed perform their own expansion of escape characters, but GNU grep doesn't deserves emphasis IMHO: I can't believe I never realized that! [Hangs head in shame] – steeldriver Nov 16 '14 at 16:18
1

You should probably realize that there's no single type of regular expressions. There are at least basic regular expressions or BRE (sometimes only RE), extended regular expressions or ERE and perl compatible regular expressions or PCRE. All those languages use slightly different syntax. Current versions of GNU grep support all three and the BRE are default. For ERE you need to use -E option and for PCRE -P option. Your example will work only with -P since with basic and extended RE the backslash loses its meaning and [\t] matches either backslash or character t. You were probably using that pattern in some other language that supports PCRE by default, which makes sense since they are the most powerful version. Or perhaps you had alias grep='grep -P' somewhere.

0

The first line works if you leave out the ^. Maybe it worked but it didn't work the way you assumed? I doubt that grep's behaviour has changed in such an important point.

echo does not translate escape sequences by default. You need the -e for that. Similar with the shell. You need $'...' to make the shell use escape sequences.

Hauke Laging
  • 90,279