Standard text search utilities like grep and ripgrep excel at finding regex patterns within source code. However, regex is blind to syntax trees: matching nested function calls, respecting variable scopes, or differentiating comments and string literals from executable logic requires parsing the code structure itself. ast-grep (invoked as sg) is a fast, polyglot command-line tool written in Rust that performs structural code search, linting, and AST-based rewriting across dozens of programming languages.
By leveraging tree-sitter grammars, ast-grep treats your code as a syntax tree while allowing you to write search queries using standard code syntax rather than complicated abstract syntax tree queries.
Installing ast-grep on Linux
ast-grep provides pre-compiled binaries, distribution packages, and package manager options across Linux distributions.
Method 1: Direct Binary Installation (Recommended)
curl -fsSL https://ast-grep.github.io/install.sh | bash
This script downloads the latest release matching your CPU architecture (x86_64 or aarch64) and places the ast-grep and sg symlinks in your local bin directory (typically ~/.local/bin).
Method 2: Using Cargo (Rust Package Manager)
If you have Rust and Cargo installed:
cargo install ast-grep --locked
Method 3: Using Homebrew or npm
# Via Linuxbrew
brew install ast-grep
# Via npm (global binary wrapper)
npm install -g @ast-grep/cli
Verify that the tool is installed and reachable in your PATH:
sg --version
Fundamental Pattern Syntax: Metavariables
In ast-grep, search patterns look almost identical to the source code you are targeting. Dynamic components are represented by metavariables:
$NAME: Matches a single AST node (such as an identifier, expression, or argument). Names must be in capital letters.$$$NAMES: Matches zero or more sequential AST nodes (such as a variable-length list of function arguments or parameters).
Performing Structural Code Searches
To search for code patterns across your project, use the sg run --pattern command (or short form sg -p):
Example 1: Finding Function Calls Regardless of Whitespace
Suppose you want to find all calls to console.log in a JavaScript or TypeScript codebase:
sg -p 'console.log($$$ARGS)'
Unlike grep, this structural query correctly finds multi-line calls, calls with complex expressions inside, and ignores occurrences inside markdown documentation or string literals.
Example 2: Matching Specific Argument Patterns
To locate where a specific error handling pattern is implemented in Python:
sg -p 'except $ERR as $E:' --lang python
Example 3: Matching Conditionals in Go
sg -p 'if err != nil { return $$$RET }' --lang go
Interactive Structural Code Rewriting
ast-grep can safely refactor code across thousands of files simultaneously using the --rewrite (or -r) flag combined with interactive mode.
Refactoring API Calls Across a Codebase
Suppose you are deprecating an old helper function fetchData(url, callback) and migrating to modern promises request(url):
sg -p 'fetchData($URL, $CB)' -r 'request($URL).then($CB)'
To review changes interactively before writing them to disk, add the -i (interactive) flag:
sg -p 'fetchData($URL, $CB)' -r 'request($URL).then($CB)' -i
During interactive review, ast-grep displays a clear colour diff for each match and prompts:
y: Accept and apply the rewrite to this occurrence.n: Skip this occurrence.a: Accept and apply all remaining matches across the codebase.q: Quit the interactive session without modifying further files.
Project-Wide Linting with ast-grep Rules
For ongoing code quality enforcement, ast-grep supports declarative YAML rule files that integrate into CI/CD pipelines:
- Initialise a project configuration:
sg new - Define a custom lint rule in
rules/no-raw-sql.yml:id: no-raw-sql-format message: "Avoid raw string formatting in SQL queries to prevent injection vulnerabilities" severity: error language: python rule: pattern: 'cursor.execute(f"$$$QUERY")' note: "Use parameterised queries such as cursor.execute(query, params) instead." - Scan the repository against the configured rules:
sg scan