# sd Advanced Patterns

Advanced find-and-replace patterns using sd (simpler than sed).

## Regex Patterns

### Capture Groups

```bash
# Reorder parts
sd '(\w+)@(\w+)\.com' '$2/$1' emails.txt
# john@example.com → example/john

# Wrap in function
sd 'console\.log\((.*)\)' 'logger.info($1)' src/**/*.js
# console.log(msg) → logger.info(msg)

# Extract and transform
sd 'class (\w+)' 'export class $1' src/**/*.ts
```

### Word Boundaries

```bash
# Match whole words only
sd '\bfoo\b' 'bar' file.txt
# "foo" matches, "foobar" doesn't

# Function rename without affecting variables
sd '\bgetUser\b' 'fetchUser' src/**/*.ts
```

### Optional and Alternation

```bash
# Optional whitespace
sd 'function\s*\(' 'const fn = (' src/**/*.js

# Match alternatives
sd '(let|var)\s+' 'const ' src/**/*.js
```

### Multiline Patterns

```bash
# Match across lines (-s flag)
sd -s 'start\n.*\nend' 'replacement' file.txt

# Remove multiline blocks
sd -s '\/\*\*[\s\S]*?\*\/' '' src/**/*.ts
```

## Real-World Patterns

### Import Transformations

```bash
# CommonJS to ES modules
sd "const (\w+) = require\('([^']+)'\)" "import $1 from '$2'" src/**/*.js

# Relative to absolute imports
sd "from '\.\./\.\./utils'" "from '@/utils'" src/**/*.ts

# Named imports reorganization
sd "import \{ (\w+), (\w+) \}" "import { $2, $1 }" src/**/*.ts
```

### API Endpoint Updates

```bash
# Version bump
sd '/api/v1/' '/api/v2/' src/**/*.ts

# Domain migration
sd 'api\.old\.com' 'api.new.com' src/**/*.ts

# Path restructuring
sd '/users/(\d+)/posts' '/posts?user_id=$1' src/**/*.ts
```

### Configuration Updates

```bash
# Environment variable rename
sd 'DATABASE_URL' 'DB_CONNECTION_STRING' .env* src/**/*.ts

# Port number change
sd 'port:\s*3000' 'port: 8080' **/*.yaml **/*.json

# Version strings
sd '"version":\s*"\d+\.\d+\.\d+"' '"version": "2.0.0"' package.json
```

### Code Modernization

```bash
# Async/await from promises
sd '\.then\(\((\w+)\)\s*=>\s*\{' 'const $1 = await ' src/**/*.ts

# Template literals
sd '"\s*\+\s*(\w+)\s*\+\s*"' '${$1}' src/**/*.ts

# Optional chaining
sd '(\w+)\s*&&\s*\1\.(\w+)' '$1?.$2' src/**/*.ts
```

### React/JSX Patterns

```bash
# className to class (or vice versa)
sd 'className="' 'class="' src/**/*.jsx
sd 'class="' 'className="' src/**/*.jsx

# Event handler rename
sd 'onClick=\{' 'onPress={' src/**/*.tsx

# Hook migration
sd 'componentDidMount\(\)' 'useEffect(() => {' src/**/*.tsx
```

### Remove Code

```bash
# Console logs
sd 'console\.log\([^)]*\);?\s*\n?' '' src/**/*.ts

# Debug statements
sd '// DEBUG:.*\n' '' src/**/*.ts

# Commented code blocks
sd '//\s*[A-Za-z].*\n' '' src/**/*.ts  # Single line comments with code

# TODO comments
sd '// TODO:.*\n' '' src/**/*.ts
```

## Batch Workflows

### Safe Preview Pattern

```bash
# 1. List affected files
rg -l 'oldPattern' src/

# 2. Preview replacements (rg with -r)
rg 'oldPattern' -r 'newPattern' src/

# 3. Apply to found files
sd 'oldPattern' 'newPattern' $(rg -l 'oldPattern' src/)

# 4. Verify
rg 'oldPattern' src/  # Should return nothing

# 5. Review changes
git diff
```

### With fd for File Selection

```bash
# TypeScript files only
fd -e ts -x sd 'old' 'new' {}

# Specific directories
fd -e js . src/ lib/ -x sd 'old' 'new' {}

# Exclude patterns
fd -e ts -E "*.test.ts" -E "*.spec.ts" -x sd 'old' 'new' {}

# By filename pattern
fd 'config' -e json -x sd '"dev"' '"prod"' {}
```

### Multiple Replacements

```bash
# Chain with &&
sd 'pattern1' 'replacement1' file.txt && \
sd 'pattern2' 'replacement2' file.txt && \
sd 'pattern3' 'replacement3' file.txt

# Or use a script
#!/bin/bash
files=$(rg -l 'oldApi' src/)
for file in $files; do
    sd 'oldApi\.get' 'newApi.fetch' "$file"
    sd 'oldApi\.post' 'newApi.send' "$file"
    sd 'oldApi\.delete' 'newApi.remove' "$file"
done
```

## Special Characters

### Escaping Reference

| Character | Escape | Example |
|-----------|--------|---------|
| `.` | `\.` | `sd '1\.0' '2.0'` |
| `*` | `\*` | `sd '\*important\*' '**important**'` |
| `?` | `\?` | `sd 'what\?' 'what!'` |
| `[` `]` | `\[` `\]` | `sd '\[x\]' '[✓]'` |
| `(` `)` | `\(` `\)` | `sd 'func\(\)' 'func(arg)'` |
| `{` `}` | `\{` `\}` | `sd '\{0,1\}' '?'` |
| `$` | `\$` | `sd '\$100' '€100'` |
| `^` | `\^` | `sd '\^note' 'NOTE'` |
| `\` | `\\` | `sd '\\n' '\n'` |
| `\|` | `\|` | `sd 'a\|b' 'a or b'` |

### Literal Mode

```bash
# When you have many special chars, consider preprocessing
# or use fixed-string replacement with rg first

# For checking matches
rg -F '[TODO]' src/

# sd doesn't have -F, so escape carefully
sd '\[TODO\]' '[DONE]' src/**/*.md
```

## Platform-Specific

### With Git

```bash
# Only changed files
sd 'old' 'new' $(git diff --name-only)

# Staged files only
sd 'old' 'new' $(git diff --cached --name-only)

# Files changed in last commit
sd 'old' 'new' $(git diff-tree --no-commit-id --name-only -r HEAD)
```

### In Docker

```bash
# Inside container
docker exec -it container sd 'old' 'new' /app/config.json

# Or with volume mount
docker run -v $(pwd):/data alpine sh -c "apk add sd && sd 'old' 'new' /data/file.txt"
```

## Tips and Best Practices

| Tip | Reason |
|-----|--------|
| Always preview with `rg -r` first | Catch mistakes before applying |
| Use git before bulk changes | Easy rollback with `git checkout` |
| Quote patterns | Prevent shell interpretation |
| Use `\b` for word boundaries | Avoid partial matches |
| Start specific, then broaden | Easier to control scope |
| Test on single file first | Verify pattern works |
| Use `fd -x` for file selection | More precise than globs |
