fedit β Fast File Editor for the Terminal

A zero-dependency CLI tool for surgical file edits from the command line.
No interactive editors. No sed/awk gymnastics. Just simple, predictable operations
with built-in verification.
Built for sysadmins, DevOps engineers, and anyone who scripts config changes.

go install github.com/amalexico/fedit@latest
Why fedit?
- One binary, zero dependencies β pure Go, runs everywhere
- 17 language mappers β see the structure of any file before editing
- -v flag β verify every mutation before moving on
- Line-aware β no regex surprises, no "which match did it hit?"
- Stream engine β process multi-GB files line-by-line with atomic integrity
- Field extraction β pull CSV/TSV column N without awk
- Safe β no in-place unless you say so, never touches files you did not name
Install
Go install (recommended):
go install github.com/amalexico/fedit@latest
Or download the binary from GitHub Releases and put it in your PATH.
Verify:
fedit -file /etc/hostname -op show
Quick Start
# See what is in a file
fedit -file config.yaml -op show
# See just lines 10-25
fedit -file config.yaml -op show -line 10 -end 25
# Find every line containing "timeout"
fedit -file config.yaml -op find -match "timeout"
# See the structure of a Go file
fedit -file main.go -op map -lang go
# Replace line 42 with new content
fedit -file config.yaml -op replace -line 42 -end 42 -text "timeout: 60s" -v
# Insert a line after every occurrence of "server {"
fedit -file nginx.conf -op insertafter -match "server {" -text " include security.conf;" -v
# Move a function block before another (content-matched, atomic)
fedit -file main.go -op move -match "func OldHelper(" -end 45 -beforematch "func NewHelper(" -v
# Copy a config block and paste it 3 times at a new location
fedit -file values.yaml -op copy -line 50 -end 65 -after 200 -times 3 -v
# Extract column 2 from a TSV file (v1.4+)
fedit -file data.tsv -op fields -col 2
# Write content with literal backslashes β no \n escape expansion (v1.6.0)
fedit -file config.txt -op writeraw -text "path=C:\\Users\\admin"
# Hex-encode tricky text to sidestep shell quoting (v1.6.0)
# fwencode produces the hex; fedit decodes it before writing
fedit -file config.txt -op write -texthex 706174683d2f746d70
# Overwrite a file cleanly then insert new content (v1.6.0)
fedit -file config.txt -op insert -line 0 -cleanfirst -text "# regenerated"
# Get bare line numbers for scripting (v1.6.0)
fedit -file main.go -op find -match "TODO" -x 2>$null
# Regex replace on a multi-GB log without loading it into memory (v1.4+)
fedit -file huge.log -op replaceall -match 'ERROR' -text 'WARN' -stream
All Operations
show β Display file contents
# Entire file
fedit -file app.conf -op show
# Lines 50-75 only
fedit -file app.conf -op show -line 50 -end 75
# Last 10 lines
fedit -file app.conf -op show -line -10:
# Lines 50 to 5th from end
fedit -file app.conf -op show -line 50 -end -5
# Lines 100-103 (relative range)
fedit -file app.conf -op show -line 100:+3
# Show from one anchor to another (no line numbers needed)
fedit -file config.go -op show -match "func Start" -endmatch "func End"
find β Search for lines matching a substring
# Find all lines containing "ERROR"
fedit -file /var/log/app.log -op find -match "ERROR"
# Output includes context lines and occurrence numbers
# Use -nth to target a specific match in other operations
Pro tip: Run find first to get line numbers, then use replace or delete with exact lines.
insert β Insert content after a line number
# Insert a comment after line 1
fedit -file script.sh -op insert -line 1 -text "# Added by deploy script" -v
# Insert multiple lines from a file
fedit -file config.yaml -op insert -line 10 -textfile extra-config.yaml -v
insertafter β Insert after a matching line (RECOMMENDED)
# Add a firewall rule after the matching comment
fedit -file iptables.rules -op insertafter -match "# Custom rules" -text "-A INPUT -p tcp --dport 8080 -j ACCEPT" -v
# Target the 2nd occurrence
fedit -file nginx.conf -op insertafter -match "server {" -nth 2 -text " listen 8443 ssl;" -v
# Target the last occurrence
fedit -file docker-compose.yml -op insertafter -match "volumes:" -nth -1 -text " - /data:/data" -v
insertbefore β Insert before a matching line (RECOMMENDED)
# Add a header before the first route definition
fedit -file routes.rb -op insertbefore -match "get '/'" -text " # === Public Routes ===" -v
# Insert a dependency before the closing bracket
fedit -file package.json -op insertbefore -match "}" -nth -1 -textfile new-deps.txt -v
replace β Replace a line range with new content
# Replace a single line
fedit -file config.ini -op replace -line 15 -end 15 -text "max_connections = 200" -v
# Replace lines 30-35 with content from a patch file
fedit -file server.conf -op replace -line 30 -end 35 -textfile patched-block.txt -v
# Replace a section by content anchors (no line numbers needed)
fedit -file CHANGELOG.md -op replace -match "## v1.6" -endmatch "## v1.5" -textfile new-section.txt -v
replaceall β Global find-and-replace
# Change all occurrences of old domain to new
fedit -file nginx.conf -op replaceall -match "old.example.com" -text "new.example.com" -v
# Update a version string everywhere
fedit -file Makefile -op replaceall -match "1.1.0" -text "1.2.0" -v
---
### fields -- Extract a column from delimited files (v1.4.0)
```bash
# Extract column 2 from a tab-separated file (default delimiter: tab)
fedit -file data.tsv -op fields -col 2
# Extract the third field from a CSV
fedit -file report.csv -op fields -col 3 -delim ","
# Extract usernames from /etc/passwd (colon-delimited)
fedit -file /etc/passwd -op fields -col 1 -delim ":"
Output goes to stdout for piping. Lines shorter than -col are skipped silently.
Always streaming -- no memory limit regardless of file size.
-stream -- Large-file streaming mode (v1.4.0)
Add -stream to replaceall or find to process files line-by-line without
loading them into memory. 10 MB per-line buffer handles JSON blobs and minified files.
Atomic integrity: writes to a temp file then renames -- original is untouched on interruption.
# Replace a pattern in a multi-GB log file
fedit -file server.log -op replaceall -match "10.0.0.1" -text "10.0.0.2" -stream
# Regex replace in a huge file with capture groups
fedit -file big.csv -op replaceall -match-regex 'id_(\d+)' -text 'ID_$1' -stream
# Streaming find -- grep-style output to stdout
fedit -file huge.log -op find -match "FATAL" -stream
Supported with -stream: replaceall (literal and regex), find.
Not supported: move, copy, map (these require full file structure in memory).
writeraw β Write without escape expansion
# Write a Windows path without double-escaping backslashes
fedit -file config.ini -op writeraw -text "basedir=C:\\Program Files\\App"
# Write content from a file as-is
fedit -file output.txt -op writeraw -textfile template.txt
Unlike write, writeraw treats \n as two characters (backslash + n), not a newline.
writelines β Write lines interactively from stdin
fedit -file notes.txt -op writelines
# Type lines at the > prompt, Ctrl+Z (Windows) or Ctrl+D (Unix) to finish
-texthex β Hex-encoded input (v1.6.0)
Encode text to hex first (e.g. with fwencode), then pass the hex string as -text.
Eliminates all shell-quoting issues with special characters.
# Decode hex string and write β no quoting gymnastics needed
fedit -file deploy.sh -op write -texthex 23212f62696e2f62617368
-matchhex / -endmatchhex β Hex-encoded anchors (v1.8.1)
Same idea as -texthex, but for the search anchors. PowerShell mangles a double quote
inside -match before fedit sees it, so case "replace": silently becomes case replace:
and matches nothing. Pass the anchor as hex instead; fedit decodes it into -match
(or -endmatch) once, so every op that takes an anchor works unchanged.
# Find a line containing double quotes (hex of: case "replace":)
fedit -file main.go -op find -matchhex 6361736520227265706c616365223a
Invalid hex exits 1 with matchhex: invalid hex string: ... (or endmatchhex: ...).
Multiple files -- comma list or glob (v1.8.1)
-file accepts a comma-separated list, a glob, or both, for the read-only ops show, find and map. Each file gets a ==> path <== header, and a real file with that exact name wins over glob expansion.
# Find TODO in every Go file in the current folder
fedit -file '*.go' -op find -match 'TODO'
# Map two files at once
fedit -file main.go,mcp.go -op map -lang go
- find skips files with no hits (grep-style) and exits 1 only if nothing matched in any file.
- map skips unrecognized extensions with a SKIP line.
- A summary line reports how many files had results.
- Mutating ops and -stream are rejected with exit 1 before any file is read. For a global rename across files, use -files GLOB with replaceall.
- CLI only: the MCP tools still take a single file.
-cleanfirst β Truncate before writing (v1.6.0)
# Clear the file then insert fresh content at line 0
fedit -file output.txt -op insert -line 0 -cleanfirst -text "# regenerated"
-x β Machine-readable output (v1.6.0)