1

This is my sample text. grep w, grep ^w and grep '^[ ]w' are working just fine.

[user@linux ~]$ grep w text.txt
whitespace 0
 whitespace 1
  whitespace 2
[user@linux ~]$

[user@linux ~]$ grep ^w text.txt
whitespace 0
[user@linux ~]$

With 1 space

[user@linux ~]$ grep '^[ ]w' text.txt
 whitespace 1
[user@linux ~]$

With 2 space, but getting the same output

[user@linux ~]$ grep '^[  ]w' text.txt
 whitespace 1
[user@linux ~]$

According to https://regex101.com/, ^[ ]{0,} is the right syntax to find whitespace at the start of a line. However, it doesn't work well with GNU grep on Linux. I'm getting the error Invalid regular expression:

[user@linux ~]$ grep ^[ ]{0,}w text.txt
grep: Invalid regular expression
[user@linux ~]$

And these do not return anything at all

[user@linux ~]$ grep '^[ ]{0}w' text.txt
[user@linux ~]$ grep '^[ ]{1}w' text.txt
[user@linux ~]$ grep '^[ ]{2}w' text.txt
[user@linux ~]$ grep '^[ ]{0,}w' text.txt
[user@linux ~]$

Question: Would it be possible to use ^[ ]{0,} with GNU grep? If yes, what was wrong with my previous syntax?

  • 1
    [ ] is a character class and it matches anything it define not a pattern to match exact double spaces. so [XXXXXX] will always match single X character not string XXXXXX. – αғsнιη May 8 at 9:56
  • To match two spaces it might be easier to use grep '^ w' (two spaces after ^). – Freddy May 8 at 9:57
  • 1
    The regex101 site does not know about POSX regular expression. See e.g. Why does my regular expression work in X but not in Y? – Kusalananda May 8 at 12:18
3

There are various issues here. First of all, the expression ^[ ]w means: find the start of the line, then exactly one space, then a w. So it's actually working perfectly. If you want it to match one or more spaces, you need to add a qualifier to the [ ] character class:

  $ grep '^[  ]\+w' text.txt
 whitespace 1
  whitespace 2

The + means "one or more". The default flavor of regular expressions used by grep are called BRE (basic regular expressions) and in that regex flavor, the + needs to be escaped, hence the \+ above*. Alternatively, you can use ERE (extended regular expressions) by passing the -E flag, or PCRE (Perl compatible regular expressions) by passing the -P flag. With these regex flavors, you don't need to escape the + for it to act as a quantifier:

$ grep -P '^[  ]+w' text.txt
 whitespace 1
  whitespace 2
$ grep -E '^[  ]+w' text.txt
 whitespace 1
  whitespace 2

The next issue, and the more important one, is that you are not quoting the regular expression. Quoting is necessary to ensure that the regex is passed to grep as is and not first interpreted by the shell. However, since you are not quoting it, it is getting expanded by the shell before it is passed to grep. You can examine this by using the set -x option to have the shell print what it is doing:

$ set -x
$ grep ^[ ]{0,}w text.txt
+ grep '^[' ']0w' ']w' text.txt
grep: Invalid regular expression

First, because there is a space between the ^[ and the ], the shell is interpreting this as two separate arguments: ^[ and ]{0,}w. But the {} are used in the shell for brace expansion. For example:

$ echo foo{a,b}
fooa foob

But when the second part of an expansion is empty, you get:

$ echo foo{a,}
fooa foo

So, the expansion ]{0,}w becomes:

$ echo ]{0,}w
]0w ]w

And as a result, and as you can see in the output of the set -x above, these three arguments are what is actually passed to grep:

'^[' ']0w' ']w'

But if you do quote them, they will need to be escaped when using BRE, just like the + above:

$ grep '^[ ]\{2\}w' text.txt
  whitespace 2

One final note: the [ ] is exactly the same as , there's no point in using a character class for a single character.

Putting all this together, to match exactly one space at the beginning of the line, use:

$ grep '^ w' text.txt 
 whitespace 1

To match one or more, use:

$ grep '^ \+w' text.txt 
 whitespace 1
  whitespace 2

Or:

$ grep -E '^ +w' text.txt 
 whitespace 1
  whitespace 2

or

$ grep -P '^ +w' text.txt 
 whitespace 1
  whitespace 2

To match a specific number range (e.g. 0, 1 or 2 spaces):

$ grep '^ \{0,3\}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

or

$ grep -P '^ {0,3}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

or

$ grep -E '^ {0,3}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

And to match a specific number, either set that number in the {} as shown above, or just repeat the character N times:

$ grep '^ \{2\}w' text.txt
  whitespace 2
$ grep '^ w' text.txt
 whitespace 1
$ grep '^  w' text.txt
  whitespace 2

And always quote your regular expressions!


*Actually, in POSIX BRE, the + has no special meaning, but the BRE implemented by GNU grep does recognize it if it is escaped.

1

In BRE, in the greedy quantifiers expression {0,}, the braces need to be escaped to achieve the required regex match and always quote your regex string. Without the quotes, the shell tries to apply its own parsing grammar to the provided arguments and in most cases the arguments get word split making grep only see the ^[ part of the regex.

grep '^[ ]\{0,\}w' file

From Regular Expression Reference: Quantifiers and choose GNU BRE

\{n,\} where n >= 0 Repeats the previous item at least n times. Greedy, so as many items as possible will be matched before trying permutations with less matches of the preceding item, up to the point where the preceding item is matched only n times.

As noted in the comments, use the * modifier which would be equivalent to using \{0,\}.

  • 1
    Using the * modifier would be equivalent to using \{0,\}. – Kusalananda May 8 at 12:18
0

Correct command :

Use grep -E '^[ ]{0,}' text.txt

-E, --extended-regexp Interpret PATTERN as an extended regular expression (ERE, see below).

Reason why its not working:

Not using single quotes around regex, bash will open it and your command will become

grep '^[' ] ]0 text.txt which translates to grep with regex '^[' on files ] , ]0 and text.txt

^[ is wrong as [ is special charater which requires closing character ] too

Why -E option:

{m,n} is an extended regular expression, and to use it grep requires -E option

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.