How to Install and Use ast-grep in Linux for Structural Code Search and Rewriting

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:

  1. Initialise a project configuration:
    sg new
  2. 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."
  3. Scan the repository against the configured rules:
    sg scan

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.