Updated: 18 October 2026 · Applies to: GNU grep 3.12 on Ubuntu 26.04, GNU grep 3.11 on Debian 13 and AlmaLinux 10.2 (the same commands work on Ubuntu 24.04 and Rocky Linux)

To grep for a tab character, let the shell insert a real tab with grep $'\t' file.txt, or use Perl syntax with grep -P '\t' file.txt. A plain grep '\t' does not work: it searches for the letter t. To match a type of character (digits, letters, spaces), put a character class inside a bracket expression, for example grep '^[[:digit:]]' file.txt lists the lines that start with a digit.

The basic form is grep [options] 'PATTERN' FILE. Always quote the pattern so the shell does not change it. Options used below: -n (line numbers), -c (count matching lines), -v (lines that do not match), -o (print only the match), -i (ignore case), -w (whole words), -E (extended regular expressions) and -P (Perl-compatible regular expressions). To search many files or whole folders, see how to find files containing text in Linux.

The sample file

All output below comes from this test file (a lab example). cat -A shows tabs as ^I and line ends as $:

$ cat -A sample.txt
server1 is online$
Server2 is offline$
2026 backups done$
^Iindented with a tab$
    indented with spaces$
# a comment line$
CAFE01 hex value$
price:^I$20$
name^Ivalue^Iunit$
email admin@example.com$
$

1. Grep for a tab character

  1. Bash, zsh and ksh turn $'\t' into a real tab before grep runs:
    $ grep -n $'\t' sample.txt
    4:	indented with a tab
    8:price:	$20
    9:name	value	unit
  2. With -P, grep understands \t itself, so it also works in shells without $'...':
    grep -nP '\t' sample.txt
    It printed the same three lines.
  3. Count the lines with a tab, or show the lines without one:
    $ grep -c $'\t' sample.txt
    3
    $ grep -cvP '\t' sample.txt
    8
  4. Only lines that start with a tab (not spaces): grep -nP '^\t' sample.txt returned only line 4.
  5. Tabs or spaces: grep '[[:blank:]]' matches either.

Why grep '\t' fails: basic and extended grep patterns have no \t escape. On AlmaLinux 10.2, grep says so and then searches for t:

$ grep -n '\t' s
grep: warning: stray \ before t
2:at

Debian 13 and Ubuntu 26.04 print no warning, they just return every line with the letter t (lines 4, 5, 6 and 9 of the sample). grep -E '\t' behaves the same, and so does [\t], because a backslash inside brackets is an ordinary character: [\t] matches a backslash or a t.

2. Character classes

A character class only works inside a bracket expression, so the brackets are doubled: [[:digit:]]. Here is what each class matched at the start of a line (grep -n '^[[:CLASS:]]' sample.txt):

  • [[:alnum:]] letters and digits: lines 1, 2, 3, 7, 8, 9, 10.
  • [[:alpha:]] letters: lines 1, 2, 7, 8, 9, 10.
  • [[:digit:]] digits 0 to 9: line 3.
  • [[:lower:]] lower-case letters: lines 1, 8, 9, 10.
  • [[:upper:]] upper-case letters: lines 2 and 7.
  • [[:xdigit:]] hexadecimal digits (0 to 9, A to F, a to f): lines 3, 7 and 10 (the "e" of "email" counts).
  • [[:punct:]] punctuation such as # ! . : @: line 6.
  • [[:blank:]] space and tab: lines 4 and 5.
  • [[:space:]] space, tab, newline, vertical tab, form feed and carriage return: lines 4 and 5.
  • [[:graph:]] visible characters (letters, digits, punctuation): every line except 4, 5 and the empty line 11.
  • [[:print:]] visible characters plus the space: the same as graph, plus line 5. The tab line is not included.
  • [[:cntrl:]] control characters: line 4, because a tab is a control character.

Leaving out the outer brackets is a common mistake, and GNU grep stops with exit status 2:

$ grep '[:digit:]' sample.txt
grep: character class syntax is [[:space:]], not [:space:]

3. Bracket expression rules

  • [abc] matches one of the listed characters; [0-9] is a range.
  • ^ as the first character negates the list: grep -n '^[^[:alnum:]]' sample.txt found the lines starting with a tab, a space or # (4, 5 and 6).
  • To match ] put it first, and put - last: grep '[]-]' matched both a]b and a-b.
  • Combine classes and characters in one bracket: [[:digit:]_-] matches a digit, an underscore or a hyphen.

4. Letters outside English and the locale

Character classes follow the locale. On Debian 13, grep 'caf[[:alpha:]]$' matched both "cafe" and "café" in the C.UTF-8 and en_US.UTF-8 locales, but only "cafe" with LC_ALL=C. In en_US.UTF-8 the equivalence class caf[[=e=]] also matched "café". In our tests [a-z] did not match an upper-case B in any of these locales, but the grep manual only defines ranges for the C locale, so [[:lower:]] and [[:upper:]] are the safer choice in scripts.

5. Shortcuts and useful patterns

  • \s (any space character) and \w (letter, digit or underscore) work in plain GNU grep. \d for a digit needs -P: grep -nP '^\d' sample.txt returned line 3, while grep -E '^\d' found nothing (AlmaLinux printed grep: warning: stray \ before d).
  • Print only numbers: grep -oE '[[:digit:]]+' sample.txt printed 1, 2, 2026, 01 and 20, one per line.
  • Empty lines: grep -n '^$'. Lines with real content: grep -cv '^[[:space:]]*$' sample.txt counted 10.
  • Trailing spaces or tabs: grep -n '[[:blank:]]$' file.
  • Windows line endings: grep -c $'\r' file counts lines that end in CRLF.
  • The second field of tab-separated lines: grep -oP '^[^\t]+\t\K[^\t]+' sample.txt printed $20 and value.

egrep and fgrep still work but are obsolescent: AlmaLinux 10.2 printed egrep: warning: egrep is obsolescent; using grep -E. Use grep -E and grep -F, as explained in the difference between grep, egrep and fgrep. For sort, cut, tr and other text tools, see Linux text processing commands.

Official documentation: GNU grep manual (character classes and bracket expressions).

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Managed dedicated servers

our engineers look after the server for you, from $135 a month.

Was this answer helpful?

Still stuck? Open a ticket