Skip to content

azhai/gx

Repository files navigation

gx

gx

A handy text-processing utility (sed/awk style): single binary, zero config, stdin/stdout friendly. Combines file search (find/list), batch rename, and classic Unix text filters (cut/trans/script) in one tool.

(This project code is generated by AI, then checked and corrected by human.)

Features

  • list: List files and directories with filters (like ls)

    • Recursive file/directory listing with glob, size, and time filters
    • Sort by name/size/mtime/ctime/path with reverse option
    • Multiple output formats: path (default), long (permissions + size + time + path), name
    • File type filtering (regular files, directories, symlinks)
    • Max recursion depth control
    • Size filter expressions (>1M, <=100K)
    • Time filter expressions (relative <=1h, >7d and absolute >=2025-01-01)
  • find: Fast file content search (inspired by ripgrep)

    • Single-process by default, multi-process via -j N / -j 0 (all cores)
    • Regular expression support
    • Colored output with highlighting
    • Line number display
    • File filtering by glob pattern
    • Binary file detection
    • .gitignore aware by default, use --all/-A to search ignored files
    • -l/--files-with-matches mode: print only file paths (like grep -l)
  • replace: Fast file content search and replace (inspired by ripgrep)

    • Single-process by default, multi-process via -j N / -j 0 (all cores)
    • Regular expression support
    • Colored output with highlighting
    • Line number display
    • File filtering by glob pattern
    • Binary file detection
    • Dry-run mode for safe preview
    • Intuitive argument parsing (2-argument smart mode)
    • .gitignore aware by default, use --all/-A to search ignored files
  • rename: Batch file renaming tool (inspired by f2)

    • Regular expression matching
    • Capture group replacement ($1, $2, etc.)
    • Dry-run mode (preview before executing)
    • Conflict detection
    • Directory support
    • Force mode for resolving conflicts
    • Case-insensitive file system support
  • cut: Extract fields from delimited text (subset of POSIX cut)

    • -f field mode only (no -c/-b)
    • Field lists: N, N-M, N- (open-ended), -M (1..M), mixed 1,3-5,7-
    • Custom delimiter (-d, supports \t \n \\ escapes)
    • -s skips lines without the delimiter
    • --output-delimiter for rearranged output
    • Reads from file or stdin (no path / - → stdin)
  • trans: Apply text transformations per line

    • upper, lower, trim, squeeze (collapse whitespace runs), reverse (rune-wise)
    • Reads from file or stdin, writes to stdout
    • Pipeline-friendly: cat f | gx trans upper | gx trans trim
  • script: Run a Tengo script over input (sed/awk style)

    • Line mode (default): injects line/lineno/filename
    • File mode (--whole): injects content/filename
    • Result convention via __out (string → emit, false → filter)
    • Safe default module whitelist; --unsafe opens all
    • Per-execution timeout (--timeout)

Project Structure

gx/
├── cmd/
├── common/           # Shared utilities (size/time expressions, gitignore)
│   ├── find/          # File content search command (grep-like)
│   ├── list/          # List files and directories with filters (ls-like)
│   ├── replace/       # File content search and replace command
│   ├── rename/        # Batch file rename command
│   ├── cut/           # Field extraction (cut -f subset)
│   ├── trans/         # Per-line text transforms (upper/lower/...)
│   └── script/        # Tengo script runner (sed/awk style)
├── processor/         # Shared walk + worker-pool engine
├── stream/            # Unified stdin/file input
├── args/              # Shared argument parsing functionality
├── regex/             # Regular expression matching wrapper
├── walker/            # Concurrent file system traversal
├── README.md          # English documentation
├── README_ZH.md       # Chinese documentation
└── LICENSE            # MIT License

Key Components

  • args: Provides common argument parsing with support for:

    • Simple 2-argument mode (pattern + path or pattern + replacement)
    • Option handling with short and long forms
    • Automatic help generation
  • regex: Wraps Go's regexp package with:

    • Case-insensitive matching
    • Fixed string support
    • Capture group replacement
  • walker: Concurrent file system traversal with:

    • Directory skipping (.git, node_modules, etc.)
    • .gitignore aware traversal (lazy loading, multi-level inheritance)
    • Glob pattern filtering
    • Binary file detection
    • Max depth control
  • common: Shared utilities:

    • sizeexpr: Size filter expression parser (>1M, <=100K)
    • timeexpr: Time filter expression parser (relative <=1h, absolute >=2025-01-01)
    • gitignore: .gitignore rule parser and matcher
  • processor: Shared file-processing engine:

    • Worker-pool pipeline over walker output
    • FileProcessor interface (ProcessFile + HandleResult) reused by find/replace
  • stream: Unified stdin/file input:

    • IsStdin / OpenInput / ReadAll
    • Virtual filename <stdin> for match reporting (used by later commands)

Installation

go install github.com/azhai/gx@latest

Or build from source:

git clone https://github.com/azhai/gx.git
cd gx
make build

Usage

gx - A handy text-processing utility (sed/awk style)

Usage: gx <command> [OPTIONS] [ARGS...]

Commands:
  find     Search for patterns in files (like grep)
  list     List files and directories with filters (like ls)
  replace  Search and replace text in files
  rename   Batch rename files
  cut      Extract fields from delimited text (like cut -f)
  trans    Apply text transformations (upper/lower/trim/...)
  script   Run a Tengo script over input (sed/awk style)

Global Flags:
  -h, --help       Show help
  -V, --version    Show version

Use "gx <command> --help" for command-specific options.

Exit Codes

Code Meaning
0 Success, matches/changes found
1 Success, but no matches / no files renamed
2 Error (bad args, IO failure, etc.)

Exit codes follow the grep convention, so gx composes naturally in shell pipelines and conditionals:

if gx find "TODO" src/; then
  echo "has TODOs"
fi

Version

gx --version      # print version + commit
gx -V             # short form

The version is injected at build time from git (see make one / make build), so a binary reports the exact tag and commit it was built from.

find - File Content Search

# Basic search
gx find "pattern"                    # Search in current directory
gx find "pattern" /path/to/dir       # Search in specific directory

# Search options
gx find -A "pattern"                 # Search all files (ignore .gitignore)
gx find -F "pattern"                 # Treat pattern as literal string
gx find -g "*.go" "func"             # Search only in Go files
gx find -i "pattern"                 # Case insensitive search
gx find -j 4 "pattern"               # Use 4 worker threads (default 1, -j 0 = all cores)
gx find -l "pattern"                 # Print only file paths with matches (like grep -l)
gx find -n "pattern"                 # Show line numbers (default)
gx find -N "pattern"                 # Hide line numbers
gx find --no-color "pattern"         # Disable colored output

# Examples
gx find "TODO" src/                  # Search for TODO in src/
gx find -i "error" -g "*.go"         # Case insensitive search in Go files
gx find "TODO" src/ test/            # Search in multiple directories
gx find --all "debug" ./project      # Search files ignored by .gitignore
gx find -l -i "error" -g "*.go" ./src  # List Go files containing "error" (case-insensitive)
gx find -j 0 "pattern" ./large-project  # Multi-threaded search using all cores
gx find -l "deprecated" -g "*.py" | xargs sed -i 's/deprecated/legacy/g'  # Pipeline

replace - File Content Search and Replace

# Basic search and replace
gx replace "pattern" "replace"       # Preview replace (dry-run)
gx replace "pattern" "replace" -x    # Execute replacement

# Using explicit options
gx replace -f "pattern" -r "replace" # Explicit find and replace
gx replace -f "TODO" -r "FIXME" -x   # Execute replacement

# Replace options
gx replace -A "pattern" "replace"    # Search all files (ignore .gitignore)
gx replace -F "pattern" "replace"    # Treat pattern as literal string
gx replace -g "*.go" "func" "FUNC"   # Replace only in Go files
gx replace -i "pattern" "replace"    # Case insensitive search
gx replace -j 4 "pattern" "replace"  # Use 4 worker threads (default 1, -j 0 = all cores)
gx replace -n "pattern" "replace"    # Show line numbers (default)
gx replace -N "pattern" "replace"    # Hide line numbers
gx replace --no-color "pattern" "replace"  # Disable colored output
gx replace -x                        # Execute replacement (default: dry-run)

# Examples
gx replace "TODO" "FIXME"            # Preview: replace TODO with FIXME
gx replace "foo" "bar" -x            # Execute: replace all 'foo' with 'bar'
gx replace -F "[test]" "demo" -x     # Replace literal string '[test]'
gx replace -i "error" "warning" -g "*.go" -x  # Case insensitive in Go files
gx replace --all "old" "new" ./project  # Replace in files ignored by .gitignore
gx replace "TODO" "FIXME" src/ test/     # Replace in multiple directories
gx find -l "deprecated" -g "*.go" | xargs -I{} gx replace "deprecated" "legacy" -x "{}"  # Pipeline

rename - Batch File Renaming

# Basic usage
gx rename "foo" "bar"                # Preview: replace 'foo' with 'bar' (dry-run)
gx rename "foo" "bar" /path/to/dir   # With specific directory

# Using explicit options
gx rename -f "pattern" -r "replace"  # Explicit find and replace
gx rename -f "foo" -r "bar" -x       # Execute rename

# Options
gx rename -d                         # Include directories
gx rename -F "pattern" "replace"     # Treat pattern as literal string
gx rename -g "*.jpg" "pattern" "replace"  # Filter by file pattern
gx rename -i "pattern" "replace"     # Case insensitive matching
gx rename -x                         # Execute (default: dry-run)
gx rename --force                    # Force rename even with conflicts

# Examples
gx rename "foo" "bar"                # Preview: replace 'foo' with 'bar'
gx rename "foo" "bar" -x             # Execute: replace 'foo' with 'bar'
gx rename "\.txt$" ".md" -x          # Change .txt to .md extension
gx rename "(\d+)" "prefix_$1" -x     # Add prefix to numbers
gx rename -i "IMG" "img" -g "*.jpg"  # Case conversion for jpg files
gx rename "^" "2024_" -x             # Add date prefix to all files
gx rename -F "[test]" "demo" -x      # Replace literal string '[test]'

list - List Files and Directories with Filters

list provides ls-like recursive file/directory listing with powerful filtering, sorting, and formatting options.

# Basic listing
gx list                              # List all files in current directory (recursive)
gx list /path/to/dir                 # List files in specific directory

# Filter options
gx list -g "*.go"                    # Filter by glob pattern
gx list -t f                         # Type: f=file, d=directory, l=symlink, a=all (default)
gx list -S ">1M"                     # Size filter: >1M, <=100K, 1024, etc.
gx list -M "<=1h"                    # Mtime filter: relative (<=1h, >7d) or absolute (>=2025-01-01)
gx list --ctime "<=1d"               # Ctime filter: same syntax as mtime

# Sort and format
gx list -s name                      # Sort by: name, size, mtime, ctime, path
gx list -s size -r                   # Reverse sort order
gx list --format long                # Long format: permissions, size, mtime, path
gx list --format name                # Name only format
gx list -L 2                         # Max recursion depth (0 = unlimited)

# Examples
gx list ./src                        # Recursively list all files in src/
gx list -g "*.go" --size ">1K" --mtime "<=1d" ./src  # Go files >1K modified today
gx list --format long --sort size --reverse ./data    # Long format sorted by size descending
gx list --type d --max-depth 2 ./project             # List directories up to depth 2
gx list -g "*.log" -S ">100M" /var/log               # Find log files larger than 100MB
gx list -M "<=1h" -t f ./src                         # Files modified in the last hour

cut - Extract Fields from Delimited Text

cut is a subset of POSIX cut: it supports -f (field mode) only. Reads from a file or stdin (no path / - → stdin), writes to stdout.

# Field selection
echo "a,b,c" | gx cut -f 2 -d ,                # → b
echo "a,b,c,d" | gx cut -f 2-3 -d ,            # → b,c
echo "a,b,c,d,e" | gx cut -f 2- -d ,           # → b,c,d,e (open-ended)
echo "a,b,c,d" | gx cut -f -2 -d ,            # → a,b (1..2)
echo "a,b,c,d,e" | gx cut -f 1,3-4 -d ,        # → a,c,d (mixed)

# Tab-delimited (default delimiter)
printf "x\ty\tz\n" | gx cut -f 2               # → y

# Skip lines without the delimiter
printf "a,b\nno-delim\nc,d\n" | gx cut -f 1 -d , -s  # → a\nc

# Custom output delimiter
echo "a,b,c" | gx cut -f 1,3 -d , --output-delimiter=:  # → a:c

# From a file
gx cut -f 1 -d , data.csv

Field spec syntax (1-based, like POSIX cut):

  • N single field
  • N-M inclusive range
  • N- open-ended (N to end of line)
  • -M 1 to M
  • mixed comma-separated, e.g. 1,3-5,7-

Escapes supported in -d / --output-delimiter: \t \n \\.

trans - Apply Text Transformations

trans applies one of the built-in transforms to each line of input. Reads from a file or stdin (no path / - → stdin), writes to stdout.

echo "hello" | gx trans upper                 # → HELLO
echo "  Hi  " | gx trans trim                 # → Hi
echo "  a   b   c  " | gx trans squeeze       # → a b c
echo "abc" | gx trans reverse                # → cba

# From a file
gx trans lower names.txt

# Pipeline (chained transforms)
cat file | gx trans trim | gx trans lower

Available transforms:

Transform Effect
upper Uppercase all letters
lower Lowercase all letters
trim Strip leading/trailing whitespace
squeeze Collapse runs of whitespace to a single space, trim
reverse Reverse the string rune-wise (Unicode-safe)

script - Run a Tengo Script Over Input (sed/awk style)

script runs a Tengo script over the input text — a lightweight alternative to sed/awk for ad-hoc transforms. The script compiles once and executes per line (default) or per file (--whole). The result goes into the __out variable; gx decides what to do based on its type (string → emit, false/undefined → filter, other → fmt.Sprint).

Modes:

  • Line mode (default): injects line (string), lineno (int, 1-based), filename (string, <stdin> for stdin).
  • File mode (--whole): injects content (whole file string), filename (string).
# -e: inline expression; safe modules (text/json/...) are pre-imported.
echo "hi" | gx script -e 'text.to_upper(line)'          # → HI

# Filter: drop even lines
seq 4 | gx script -e 'lineno % 2 == 1 ? line : false'

# Line number prefix using fmt.sprintf
seq 3 | gx script -e 'fmt.sprintf("%d: %s", lineno, line)'

# Whole-file mode: count newlines
printf 'a\nb\nc\n' | gx script --whole -e 'text.count(content, "\n")'  # → 3

# -f: load a full script (you control __out yourself)
gx script --whole -f agg.tengo *.log

Security model — by default only pure-computation Tengo modules are available: fmt, text (strings/strconv/regexp), json, math, times, base64, hex, enum. --unsafe opens all modules (os/exec/file/...) — only use with trusted input.

-e automatically pre-imports every safe module so expressions like text.to_upper(line) work without an explicit import line. -f scripts must import modules themselves.

Other flags: --timeout D (per-execution timeout, default 1s).

Full module/API reference: docs/script-api.md.

Build from Source

git clone https://github.com/azhai/gx.git
cd gx
make one       # Build native binary (bin/gx)
make build     # Cross-compile all platforms
make release   # Cross-compile + SHA256SUMS

Cross-compile outputs are versioned with the git tag and short commit:

bin/gx-<version>-<os>-<arch>     # e.g. bin/gx-v0.2.0-darwin-arm64

Supported targets: darwin-arm64, darwin-amd64, linux-arm64, linux-amd64, windows-amd64.

The version and commit are injected via -ldflags, so ./bin/gx --version reports the exact tag and commit it was built from.

Other Makefile targets:

make clean     # Remove old binaries
make upx       # Build and compress with upx
make upxx      # Build and compress with upx --ultra-brute

Running Tests

go test ./... -v

License

MIT License - see LICENSE file for details.

Acknowledgments

  • find/replace inspired by ripgrep
  • rename inspired by f2

About

A fast file search and batch rename tool written in Go.

Resources

License

Stars

4 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors