# cgrep searches by what code is, not only by what it says

> cgrep is a Haskell rewrite of grep that parses source code, so a pattern can match identifiers, keywords, native types, operators or string literals, and test files can be excluded from a search across 29 languages. It suits a developer who searches a large codebase and keeps landing on matches inside test files, and it is a Haskell tool with no binary distribution listed in the docs.

**awgn/cgrep** — Cgrep: a context-aware grep for source codes

- Repository: https://github.com/awgn/cgrep
- Stars: 743 · Forks: 39
- Language: Haskell
- License: GPL-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/awgn-cgrep

## grep with a parser attached

cgrep is described as a context-aware grep for source codes, extending the capabilities of traditional grep by understanding the structure and semantics of source code across multiple programming languages. The point is that a match can be constrained to a kind of token rather than to a string. You can search in code, in comments or in string literals, and separately for identifiers, keywords, native types, operators, literal numbers and literal strings. That distinction is what makes it different from a plain grep, where a search for a name matches the same name in a comment, in a string, in a call and in a declaration, and you filter the noise afterwards by eye. The primary language is Haskell, the package is cgrep on Hackage, and the project describes itself as a tool designed specifically for source code. The version at the time of writing is 9.3.1, and the licence is GPL-2.0, with a CHANGELOG.md at the repository root tracking the sequence of releases.

## Semantic patterns name the token you want

The -S, or semantic, mode is the core of the tool. The usage text lists what the mode understands: _, _1, _2 and so on for identifiers,..., matching zero or more tokens, KEY for keywords, TYP for native types, OP for operators, STR for string literals, LIT for any string or numeric literal, NUM for numbers, HEX, OCT, OR and NOT. The three additions in 9.3.1 are the ones that change the daily experience. TYP matches native types, OP matches operators, and LIT matches any string or numeric literal, which is distinct from STR, that matches only string literals. The structural ellipsis operator,..., matches zero or more tokens across expressions, argument lists or statements while automatically respecting balanced brackets and statement boundaries, so a pattern can say call something with a list of arguments without spelling out the arguments. The semantic combinators OR and NOT let you combine atoms directly, with examples like TYP OR KEY _ (... ), _ = NOT STR and STR OR NUM. Placeholder unification has also been improved, with numbered placeholders and preserved leading underscores on ordinary identifiers such as _foo and __init__, which is the detail that stops a search for a name from matching every dunder method in a Python file.

## Test filtering across 29 languages

The second large feature is excluding test code from a search, which sounds trivial until you work in a repository with a large test suite. The --tests-only and --no-tests flags, with -T as the short form, do that, and they replace the older -T True and -T False options that the 9.2.1 release notes describe as somewhat awkward. The detection is per language, refactored in 9.3.0 into a type-safe per-language architecture. The list of recognised frameworks is long and specific. Lua has Busted, Lust and LuaUnit, Elm has elm-test, and Bash has BATS, shUnit2 and bashunit. The 9.3.0 release also added Kotest for Kotlin, ZIO Test and Specs2 for Scala, Pest and the PHP 8 test attributes, Swift Testing macros along with Quick and Nimble, Deno.test and the each and concurrent chains for JavaScript and TypeScript, Doctest, RUnit for R, the Test::Class and Test::Spec forms for Perl, Common Test for Erlang, and Midje for Clojure. The same release worked on false positives, with the specific cases named, including the identifier testify, the @Testimonial annotation, the Testable attribute, forward and abstract declarations, field accesses such as regex.test() and #state.value, and the index expression [1:end].

## Installing it, and the file type and kind switches

The installation paths are the standard Haskell ones. From Hackage it is cabal update followed by cabal install cgrep:

```bash
cabal update
cabal install cgrep
```

From source you clone the repository and run cabal install, or use stack:

```bash
git clone https://github.com/awgn/cgrep.git
cd cgrep
cabal install
```

```bash
stack build
stack install
```

The repository holds a cgrep.cabal file, a Setup.hs, a stack.yaml with a lock file, a cgreprc configuration file, the src/ directory, test/ and test_suite/ directories, a run_all_tests.sh script and a run_validation.js script. The option list is long, but a few matter more than the rest. --type selects file types, with a syntax like Cpp, +Haskell, -Makefile, so you can add or exclude types, and --type-list prints what is supported. --kind selects file kinds among Text, Config, Language, Data, Markup and Script. --code-only parses code modules and skips headers and interfaces, while --hdr-only does the opposite, which is the pair you want in a language where the interface and the implementation carry different names. Other options worth knowing are --prune-dir to avoid descending into a directory, -L to follow symlinks where -r does not, --max-count to stop after a number of matches in a file, -j to set the approximate thread count, and --json for machine readable output alongside the usual --count, --filename-only and --no-column.

## Performance work in 9.2 and what the numbers mean

The 9.2 release is the one that reports measurements, and the numbers are worth reading in context. Plain token search is 75 percent faster and semantic search is 39 percent faster, and the same release switched from bytestream to text processing, which brought full UTF-8 support and accurate column positions for international characters. That last part is the change that matters most in practice, because a tool that reports column offsets correctly for non-ASCII source is a tool you can drive from an editor. The byte oriented version could not give you a column number that meant anything in a file with multi-byte characters. The performance figures are described as dramatic and significant in the release notes, which is the project's own framing rather than a benchmark you can rerun, so treat them as directional. The switch to text processing is a plausible explanation for a slowdown in the search itself, offset by the parser work being done anyway.

## Where cgrep does not help

The limits follow from the approach. A context-aware grep has to keep up with the languages you use, and the language list is a fixed set of 29 rather than an open set, so a project using a language outside it gets no semantic behaviour and no test filtering for that language. The 9.3.0 notes even record the practical effect of a language whose grammar the parser handles imperfectly, since the false positive hardening work covers forward and abstract declarations and record and method field accesses across all 29 languages in both default and strict modes, which tells you how much of that work is inherent to the approach. The strict flag itself is a sign of the same thing, since --strict enables strict semantic matching for operators, which is a way of saying the default behaviour has edge cases. There is also a simple operational point. cgrep is a Haskell program, so there is no prebuilt binary listed in the documentation, and installing it means a Haskell toolchain or a package manager, which is a heavier prerequisite than a single static binary would be.

## Conclusion

Adopt cgrep when you search a large multi-language codebase and your main annoyance is either matching the wrong kind of token or matching inside test files, and when you are comfortable with a Haskell toolchain. Do not adopt it as a general grep replacement, since the parsing has to keep up with the languages you use. Verify first that your languages are in the supported set and that the test framework detection covers the ones you use, that the semantic pattern syntax in the -S mode is what you want, since a pattern without the semantic flag still behaves like ordinary grep, and that you install it from Hackage or build it with a working Haskell toolchain.

## FAQ

### How do I install cgrep?

From Hackage it is cabal update followed by cabal install cgrep. From source, clone the repository and run cabal install, or use stack build and stack install. The project has no prebuilt binary listed in the documentation, so a Haskell toolchain is required.

### What can the cgrep semantic search mode match?

In -S mode the pattern language includes _ and numbered placeholders for identifiers, ... for any token, KEY for keywords, TYP for native types, OP for operators, STR for string literals, LIT for any string or numeric literal, NUM for numbers, HEX, OCT, OR and NOT, with 9.3.1 adding the semantic wildcards, the structural ellipsis and the OR and NOT combinators.

### Can cgrep skip test files when searching?

Yes. The --tests-only and --no-tests flags, with -T as the short form for the former, filter by test file, replacing the older -T True and -T False options. The detection covers 29 languages and frameworks including Busted and shUnit2, elm-test, Kotest, ZIO Test, Pest, Swift Testing, RUnit, Common Test and Midje.

### Which cgrep options control which files are searched?

The --type option selects file types with a syntax like Cpp, +Haskell, -Makefile, and --type-list prints the supported types. The --kind option selects file kinds among Text, Config, Language, Data, Markup and Script, while --code-only and --hdr-only parse only code modules or only headers and interfaces, and --prune-dir stops the search descending into a directory.

## Sources

- [awgn/cgrep on GitHub](https://github.com/awgn/cgrep)
- [Issues](https://github.com/awgn/cgrep/issues)
- [License: GPL-2.0](https://github.com/awgn/cgrep/blob/master/LICENSE)
- [README](https://github.com/awgn/cgrep/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/awgn-cgrep
