Skip to content

Essential CLI Tools: jq, fzf, ripgrep and fd

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn essential CLI tools including jq for JSON processing, fzf for fuzzy finding, ripgrep for fast search, and fd for file finding that supercharge your terminal workflow.

Why CLI Tools Matter

The standard Unix tools (grep, find, awk) have served developers for decades, but modern alternatives offer dramatic speed improvements, better defaults, and more intuitive interfaces. Replacing grep with ripgrep, find with fd, and using jq for JSON processing can cut your command execution time from seconds to milliseconds.

By the end of this guide, you will use jq, fzf, ripgrep, and fd fluently, and integrate them into your daily workflow.

jq: JSON Processing

jq is a lightweight and flexible command-line JSON processor. It slices, filters, maps, and transforms structured data with minimal syntax.

flowchart LR
  A[JSON Input] --> B[jq Filter]
  B --> C[Filtered Output]
  C --> D[Select Fields]
  C --> E[Transform Data]
  C --> F[Format Output]

Installation

# macOS
brew install jq

# Linux
sudo apt install jq -y

Basic Usage

# Pretty-print JSON
echo '{"name":"Alice","age":30,"city":"New York"}' | jq .

Expected Output

{
  "name": "Alice",
  "age": 30,
  "city": "New York"
}

Selecting Fields

# Select a single field
echo '{"name":"Alice","age":30}' | jq '.name'
# "Alice"

# Select multiple fields
echo '{"name":"Alice","age":30,"city":"NYC"}' | jq '{name, city}'
# { "name": "Alice", "city": "NYC" }

# From an array
echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '.[].name'
# "Alice"
# "Bob"

Filtering and Mapping

# Filter array by condition
cat data.json | jq '.[] | select(.age > 25)'

# Map to new structure
curl https://api.github.com/repos/stedolan/jq | jq '{owner: .owner.login, stars: .stargazers_count}'

Expected Complex Output

$ cat data.json
[
  {"name": "Alice", "age": 30, "role": "developer"},
  {"name": "Bob", "age": 25, "role": "designer"},
  {"name": "Charlie", "age": 35, "role": "developer"}
]

$ jq '.[] | select(.role == "developer") | .name' data.json
"Alice"
"Charlie"

Common Patterns

# Count items
jq 'length' data.json

# Group by field
jq 'group_by(.role) | map({role: .[0].role, count: length})' data.json

# Format output as CSV
jq -r '.[] | [.name, .age, .role] | @csv' data.json
# "Alice",30,"developer"
# "Bob",25,"designer"

fzf: Fuzzy Finder

fzf is a general-purpose command-line fuzzy finder that can be used with any list: files, command history, processes, Git branches, and more.

Installation

# macOS
brew install fzf

# Linux
sudo apt install fzf -y

# Install key bindings
$(brew --prefix)/opt/fzf/install

Basic Usage

# Fuzzy find files in current directory
find . | fzf

# Search command history
history | fzf

# Search running processes
ps aux | fzf

Key Bindings

After installing key bindings:

Key Action
Ctrl-T Paste selected file path into command
Ctrl-R Search command history
Alt-C cd into selected directory

Integration with Other Commands

# Open file from fzf in Neovim
nvim $(fzf)

# Kill a process
kill -9 $(ps aux | fzf | awk '{print $2}')

# Git checkout branch
git checkout $(git branch | fzf)

Advanced fzf Usage

# Preview file content
fzf --preview 'cat {}'

# Syntax-highlighted preview for source files
fzf --preview 'bat --color=always {}'

# Search with preview and bindings
fzf --preview 'bat --color=always {}' \
  --bind 'ctrl-e:execute(nvim {})'

ripgrep recursively searches directories for a regex pattern. It respects .gitignore rules, ignores binary files, and is significantly faster than grep.

Installation

# macOS
brew install ripgrep

# Linux
sudo apt install ripgrep -y

Basic Usage

# Search for pattern
rg "function" src/

# Search for pattern (case-insensitive)
rg -i "function" src/

# Show context lines
rg -C 3 "error" src/

# Count matches
rg -c "import" src/

Expected Output

$ rg "TODO" src/
src/main.js:10:  // TODO: implement error handling
src/utils.js:25: // TODO: add validation
src/api.js:8:    // TODO: move to config

$ rg -c "function" src/
src/main.js:3
src/utils.js:12

Smart Search Features

# Search specific file types
rg -t js "export default"
rg -t py "def __init__"

# Search in specific file extensions
rg "TODO" --glob "*.{js,ts,jsx}"

# Show file matches only (-l = list files)
rg -l "import React"

# Search everything (including hidden files)
rg -u "secret"

# Search and replace (with sed)
rg "old_function" --files-with-matches | xargs sed -i 's/old_function/new_function/g'

Performance Comparison

$ hyperfine "grep -r 'import' src/" "rg 'import' src/"
Benchmark 1: grep -r 'import' src/
  Time (mean +- sd):     456.2 ms +- 12.3 ms

Benchmark 2: rg 'import' src/
  Time (mean +- sd):      23.1 ms +- 1.2 ms

  Summary
  rg 'import' src/ ran 19.75 times faster than grep -r 'import' src/

fd: File Finder

fd is a fast and user-friendly alternative to find. It uses colorized output, respects .gitignore, and has sensible defaults.

Installation

# macOS
brew install fd

# Linux
sudo apt install fd-find -y

Basic Usage

# Find files by name
fd "config"

# Find files by extension
fd -e js
fd -e py -e rs

# Find directories
fd -t d "src"

# Find and execute command
fd "*.js" -x wc -l {}

Expected Output

$ fd "config"
src/config.js
src/config.json
tests/config.test.js
deploy/config.yaml

Advanced fd Usage

# Hidden files and directories
fd -H ".git"
fd -H ".env"

# Exclude directory
fd "log" -E "node_modules" -E ".git"

# Case-insensitive search
fd -i "README"

# Maximum depth
fd "test" --max-depth 2

# List type of each match
fd -t s "socket"  # Symlinks

Integration: All Four Together

# Find all JSON files and search within them
fd -e json | xargs rg "error"

# fzf + rg: interactive code search
rg -l "function" | fzf --preview 'bat --color=always {}'

# jq + fzf: browse JSON
cat data.json | jq '.[].name' | fzf

# fd + rg + jq: find config files, grep for API keys, parse with jq
fd -e json config/ | rg "api_key" | xargs cat | jq '.api_key'

Common Errors

Problem Cause Fix
jq: error: Cannot iterate over null Input is not an array Check JSON structure, use // empty to skip nulls
fzf: command not found fzf not in PATH Run $(brew --prefix)/opt/fzf/install or check installation
rg: error: no such file or directory Path does not exist Verify the path, use rg without path to search current dir
fd: No such file or directory Package name differs on Linux Install fd-find, binary may be fdfind
Permission denied File not readable Use -u flag with rg or sudo

Practice Questions

1. What does jq '.name' data.json do?

Extracts the name field from a JSON object.

2. How do you use fzf to select a file and open it in vim?

vim $(fzf).

3. How is ripgrep different from grep?

ripgrep is faster, respects .gitignore by default, ignores binary files, and has better defaults.

4. What command finds all Python files in the current directory using fd?

fd -e py.

5. How do you preview file content in fzf?

Use fzf --preview 'cat {}' or fzf --preview 'bat --color=always {}'.

Challenge

Create a shell function called fuzzy-grep that uses fd to find files, rg to search within them, and fzf to interactively select results. The function should accept a search pattern and return the selected file path with line number.

Real-World Task

Audit a large codebase by combining these tools. Use fd to count files by extension, rg to find all TODOs and FIXMEs, jq to parse any JSON configuration files, and fzf to navigate the results interactively. Document the audit findings using these tools exclusively.

How do I install these tools on macOS?

Use Homebrew: brew install jq fzf ripgrep fd.

Are these tools available for Windows?

Yes, through WSL (Windows Subsystem for Linux) or native Windows builds available on GitHub.

Can I use fd with fzf?

Yes. fd -t f | fzf lists files and lets you fuzzy-find them. This is faster than find . | fzf for large directories.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro