mcpField
F
fedit

fedit

本地服务
github.com/amalexico开发工具111 周前更新

关于 fedit

fedit 是面向系统管理员、DevOps 工程师及自动化脚本的 Go 命令行工具和 MCP 服务,用于精确编辑文件。它支持按行和锚点定位、操作验证,以及处理大型文件的流式操作。

主要功能

  1. 01按锚点插入、删除、替换、移动和复制
  2. 02支持验证的按行编辑
  3. 03多文件操作与跨文件传输
  4. 04大型文件的流式查找和替换
  5. 05语言结构映射与分隔字段提取

适用场景

  1. 01在部署脚本中更新配置文件
  2. 02跨多个文件编辑匹配的区段
  3. 03搜索或替换超大型日志中的内容

MCP 相关

MCP 服务提供文件查看、搜索、映射和编辑工具,也支持相应的多文件及跨文件操作。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

fedit — 终端快速文件编辑器

Ko-fi Open Collective

一款零依赖的 CLI 工具,可通过命令行对文件进行精准编辑。 无需交互式编辑器。无需折腾 sed/awk。只需简单、可预测的操作, 并内置验证功能。

专为系统管理员、DevOps 工程师以及所有需要编写配置更改脚本的人打造。

fedit 演示

go install github.com/amalexico/fedit@latest

为什么选择 fedit?

  • 单个二进制文件,零依赖 — 纯 Go 编写,随处可运行
  • 21 种语言映射器 — 编辑前查看任意文件的结构
  • -v 标志 — 每次修改后都进行验证,再继续操作
  • 按行处理 — 不会遇到正则表达式意外,也不用猜“匹配到了哪一处?”
  • 流式引擎 — 逐行处理数 GB 的文件,并保证原子完整性
  • 字段提取 — 无需 awk 即可提取 CSV/TSV 的第 N 列
  • 一次处理多个文件 — 在 -file 中使用逗号分隔列表或 glob,还可通过 -dest 跨文件复制、移动和替换(自 v1.9.0 起)
  • 安全 — 除非明确指定,否则不会原地修改;绝不触碰未指定的文件

安装

通过 Go 安装(推荐):

go install github.com/amalexico/fedit@latest

或者从 GitHub Releases 下载二进制文件,并将其放入 PATH。

验证:

fedit -file /etc/hostname -op show

快速开始

# 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

所有操作

show — 显示文件内容
# 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 — 搜索包含指定子字符串的行
# 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

专业提示: 先运行 find 获取行号,再使用 replace 或 delete 精确指定行进行操作。


insert — 在指定行号后插入内容
# 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 — 在匹配行后插入内容(推荐)
# 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 — 在匹配行前插入内容(推荐)
# 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 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 — 全局查找并替换
# 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 -- 从分隔符文件中提取一列(v1.4.0)
# 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 ":"

输出会写入 stdout,便于通过管道传递。短于 -col 指定列数的行会被静默跳过。 始终采用流式处理——文件大小不受内存限制。

-stream -- 大文件流式处理模式(v1.4.0)

为 replaceall 或 find 添加 -stream,即可逐行处理文件,而无需将其载入内存。每行 10 MB 的缓冲区可处理 JSON 块和压缩文件。 原子完整性:先写入临时文件,再进行重命名——中断时不会影响原文件。

# 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

支持 -stream 的操作:replaceall(字面匹配和正则表达式)、find。 不支持:move、copy、map(这些操作需要将完整文件结构载入内存)。

writeraw — 不展开转义字符,直接写入
# 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

与 write 不同,writeraw 会将 \n 视为两个字符(反斜杠 + n),而不是换行符。


writelines — 从 stdin 交互式写入行
fedit -file notes.txt -op writelines
# Type lines at the > prompt, Ctrl+Z (Windows) or Ctrl+D (Unix) to finish

-texthex — 十六进制编码输入(v1.6.0)

先将文本编码为十六进制(例如使用 fwencode),然后将十六进制字符串作为 -text 传入。 彻底避免所有 shell 引号问题。

# Decode hex string and write — no quoting gymnastics needed
fedit -file deploy.sh -op write -texthex 23212f62696e2f62617368

-matchhex / -endmatchhex — 十六进制编码锚点(v1.8.1)

与 -texthex 相同的思路,但用于搜索锚点。PowerShell 会在 fedit 看到 -match 中的双引号前将其破坏,因此 case "replace": 会悄无声息地变成 case replace:,导致没有任何匹配。改用十六进制传入锚点;fedit 会将其解码为 -match (或 -endmatch),因此所有接受锚点的操作都能照常使用。

# Find a line containing double quotes (hex of: case "replace":)
fedit -file main.go -op find -matchhex 6361736520227265706c616365223a

无效的十六进制字符串会以状态码 1 退出,并显示 matchhex: invalid hex string: ...(或 endmatchhex: ...)。


多个文件——逗号分隔列表或 glob(show/find/map 自 v1.8.1 起支持,修改操作和 MCP 自 v1.9.0 起支持,-sourcefile 和 -sourceblock 搭配列表自 v1.11.0 起支持)

-file 接受逗号分隔列表、glob,或两者兼用。若存在名称完全匹配的实际文件,则优先使用该文件,不进行 glob 展开。

只读操作(show、find、map)会为每个文件打印一个 ==> path <== 标题:

# 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 会跳过没有命中项的文件(类似 grep),且仅在所有文件都没有匹配项时以状态码 1 退出。
  • map 会跳过无法识别扩展名的文件,并输出一行 SKIP。
  • 摘要行会报告有结果的文件数量。

修改操作(replaceall、insertafter、insertbefore、delete、replace、write)会分别对每个文件单独执行:

# Replace in every matching file
fedit -file '*.conf' -op replaceall -match 'old.example.com' -text 'new.example.com'

# Insert after the first match in each file
fedit -file a.yaml,b.yaml -op insertafter -match 'servers:' -text '  - new'

# Delete from a start line through an end line in each file (-endmatch is inclusive)
fedit -file '*.md' -op delete -match '' -endmatch ''
  • 某个文件出现问题(无匹配项、无法读取、只读、写入失败)时,会报告为 SKIP,且不会中止批处理。被跳过的文件绝不会被修改。
  • 报告会逐个列出每个文件的 OK 或 SKIP,然后显示 N/M file(s) succeeded。只要至少有一个文件成功,退出代码就是 0;如果没有文件成功,则为 1。
  • 只接受 -match(delete 和 replace 还接受 -endmatch)、-nth 和 -text/-textfile/-texthex,以及 insertafter 和 insertbefore 中的 -sourcefile/-sourceblock/-lang(自 v1.11.0 起)。replaceall 支持 -match-regex;write 无需 -match,会向每个文件写入相同内容。-block、-line、-cleanfirst、-stream 和 -files 均不受支持,move 和 copy 也不受支持;这些参数会在读取任何文件之前被拒绝。
  • MCP:fedit_replaceall、fedit_insertafter、fedit_insertbefore、fedit_delete 和 fedit_replace 的 file 参数接受逗号分隔列表或 glob,规则相同,并会逐个文件生成报告;block、line、stream 和 files 不受支持。fedit_write 始终将 file 视为单一路径。fedit_show、fedit_find 和 fedit_map 也接受列表。
跨文件复制、移动和替换——-dest(v1.9.0,MCP 从 v1.10.0 起支持)

-dest 会将 -file 中的代码块复制或移动到一个或多个其他文件中。它接受以逗号分隔的现有文件列表(不支持 glob)。源内容按常规方式选取:使用 -line/-end、-match/-endmatch,或使用带 -lang 的 -block。目标锚点会在每个目标文件中单独解析,使用 -after、-before、-aftermatch、-beforematch、-afterblock 或 -beforeblock 之一(块锚点需要 -lang)。

# Copy a function into two other files, right after a named block in each
fedit -file a.go -op copy -block Helper -lang go -dest b.go,c.go -afterblock Init

# Move a config section into two files, before a marker line in each
fedit -file base.conf -op move -match '[cache]' -endmatch '[/cache]' -dest x.conf,y.conf -beforematch '[logging]'

# Paste 2 copies in each destination
fedit -file src.txt -op copy -line 5:+3 -dest a.txt,b.txt -aftermatch ANCHOR -times 2
  • 每个目标都会单独处理。某个目标出现问题(文件不存在、未找到锚点、只读、写入失败)时,会报告为 SKIP,且不会修改该文件。批处理不会中止。
  • move 会先写入所有目标。只有至少一个目标写入成功时,才会移除源文本。如果所有目标都失败,源文件会保持不变,因此文本绝不会丢失。
  • 报告会列出每个目标的 OK 或 SKIP,随后显示 N/M file(s) succeeded。至少有一个目标成功时,退出码为 0;全部失败时,退出码为 1。
  • 如果目标文件就是源文件,则会被标记为 SKIP;同一个文件在列表中出现多次时,只会使用一次。
  • copy 和 move 使用带锚点标志的 -dest,replace 使用带 -destblock 的 -dest(见下文)。与任何其他 op 以及 -match-regex、-cleanfirst、-stream 或 -files 一起使用时,命令会被拒绝,且不会读取任何文件。它不能与多文件 -file 列表组合使用:-file 必须是单个源文件。
  • 跨文件传输的是文本,而不是依赖项。调用 fmt.Println 的 Go 函数会原样写入没有 fmt 导入的文件中,而该文件在添加导入之前无法编译。从 v1.11.0 起,如果 Go 目标文件缺少传输行所用的标准库导入,使用 -dest 的 copy、move 和 replace 会添加提示(仅供参考:结果和退出码不变)。
  • MCP:fedit_copy、fedit_move 和 fedit_replace 接受 dest 参数(文件逗号分隔列表),并使用相同的锚点;replace 还接受 destblock。规则相同,并会按目标分别报告;file 必须是单个路径。fedit_insertafter 和 fedit_insertbefore 接受 sourcefile 和 sourceblock(需带 lang);其中 file 也可以是逗号分隔列表或 glob。

替换其他文件中的代码块——replace -dest 和 -destblock

replace -dest 会用 -file 中的源代码块或范围覆盖每个目标文件中的指定代码块。-destblock 用于指定要覆盖的代码块,且为必填项。源内容的选取方式与 copy 和 move 相同,-lang 用于解析代码块名称。

# Overwrite Helper in two files with the Helper from a.go
fedit -file a.go -op replace -block Helper -lang go -dest b.go,c.go -destblock Helper -v
  • 如果目标文件中没有该代码块,则会标记为 SKIP,绝不会改为插入。每个目标都会像 copy 和 move 一样单独处理。
  • 仅在使用 replace -dest 时才接受 -destblock。

从其他文件插入代码块——-sourcefile 和 -sourceblock

insertafter 和 insertbefore 可以从另一个文件中指定的代码块获取内容。-file 仍然是要编辑的文件,锚点按常规方式选取。

# Insert Helper from a.go into b.go, after the line matching '// helpers'
fedit -file b.go -op insertafter -match '// helpers' -sourcefile a.go -sourceblock Helper -lang go -v
  • -sourcefile 和 -sourceblock 必须同时使用,并且需要 -lang。它们不能与 -text、-textfile 或 -texthex 组合使用。
  • -file 可以是逗号分隔列表或 glob(从 v1.11.0 起支持)。代码块会从 -sourcefile 读取一次,然后插入到每个文件各自的 -match 位置,并按文件分别报告。未找到匹配项的文件会被标记为 SKIP,且不会修改。这不是 -dest 扇出:每个目标都在自身的 -match 位置进行编辑。
  • 从 v1.11.0 起,如果源文件和目标文件都是 Go 文件,且插入的代码块使用了目标文件未导入的标准库包,则会打印提示(CLI 中输出到 stderr,MCP 中显示在 OK 消息中;仅供参考:仍会执行插入,退出码不变)。使用文件列表时,提示会添加到对应文件的报告行中。
  • MCP 工具 fedit_insertafter 和 fedit_insertbefore 接受 sourcefile 和 sourceblock 参数(需带 lang)。

guided——自然顺序入口点(CLI,自 v1.11.0 起)

fedit guided 按自然顺序接收源、动词和目标,将它们转换为显式的 -op 命令,并在运行前将其打印到 stderr。它只是 CLI 快捷方式:没有对应的 MCP 工具,引擎和显式标志均保持不变。

fedit guided SRCFILE SRCBLOCK copy|move DESTFILE [before TARGET | after TARGET | at-end] [silent|report-only]
fedit guided SRCFILE SRCBLOCK replace DESTFILE [TARGET]
fedit guided SRCFILE SRCBLOCK insertbefore|insertafter DESTFILE TARGET
fedit guided "new text" insertbefore|insertafter FILE TARGET
fedit guided

fedit guided a.go Helper copy b.go before Init
fedit guided a.go Helper move "b.go,c.go" after Init
fedit guided a.go "line 12-18" copy b.go at-end
fedit guided a.go Helper replace b.go
  • 源可以是一个命名代码块(语言根据文件扩展名确定),也可以是 "line N-M"。
  • 多个目标:将它们作为一个逗号分隔列表加引号("b.go,c.go"),否则 PowerShell 会将它们拆开。
  • 每个目标都会单独处理。缺失的文件会被创建;锚点缺失(或未指定位置)时,会追加到末尾,并附带说明。移动操作会先执行复制,最后再移动,因此只有写入成功后才会删除源。
  • 代码块名称会按子字符串匹配,与其他地方的规则相同。如果名称匹配多个代码块,则优先选择唯一的精确匹配(并附带说明);否则,在终端中会显示带编号的选项,没有终端时则会显示候选列表并以退出代码 2 结束。随后会通过行号定位所选代码块。
  • replace 会在每个目标中覆盖 TARGET(默认与源名称相同),并跳过不包含该代码块的目标。
  • silent 使用 -quiet 运行计划:不回显、不显示说明;如果所有文件均成功,则不显示引擎报告(跳过和错误仍会显示)。
  • report-only 运行计划时不显示验证输出和回显行,但会保留说明和引擎自身的摘要。这两个修饰符都放在最后,并且适用于所有动词。
  • 单独运行 fedit guided(不带其他参数)时,会依次询问:操作、源文件和代码块、目标、位置,然后是修饰符。操作和位置可以按名称或编号回答;选错时会重新询问(最多三次)。提示会发送到 stderr,答案从 stdin 逐行读取,因此支持管道输入。如果输入在最后一个答案之前结束,则不会进行任何更改。
-cleanfirst——写入前截断(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——机器可读输出(v1.6.0)
# Get bare line numbers from find (stdout only, no context noise)
fedit -file main.go -op find -match "TODO" -x 2>$null

# Extract CSV column with no stats footer
fedit -file data.csv -op fields -col 2 -delim "," -x

v1.5.0:HCL/Terraform 代码块映射器(-lang hcl)

按名称移动、复制和重构 Terraform 代码块,无需行号。 接受 -lang hcl、-lang tf 或 -lang terraform(三者等效)。

支持的代码块类型:resource、data、module、provider、variable、 output、locals、terraform、moved、import、check。

# Move a resource block before another
fedit -file main.tf -op move -block 'resource "aws_instance" "web"' \
      -beforeblock 'resource "aws_s3_bucket" "data"' -lang hcl -v

# Copy a variable definition (scaffold new variable from existing)
fedit -file variables.tf -op copy -block 'variable "instance_type"' \
      -after 20 -lang hcl -v

# Reorder provider blocks
fedit -file providers.tf -op move -block 'provider "google"' \
      -beforeblock 'provider "aws"' -lang hcl -v

嵌套代码块(例如 resource 中的 ingress {})会被正确忽略—— 只匹配顶层代码块。

v1.5.0:Nix 代码块映射器(-lang nix)

在 Nix 表达式文件中移动和复制顶层属性绑定。 支持属性集(name = { })、列表(name = [ ])和 点分属性(programs.git = { })。

# Reorder home-manager program configs
fedit -file home.nix -op move -block "programs.git" \
      -beforeblock "programs.ssh" -lang nix -v

# Copy a service config as a scaffold
fedit -file configuration.nix -op copy -block "services.nginx" \
      -after 50 -lang nix -v
move——将一段行移动到新位置
# Move lines 100-120 to after line 200 (explicit range)
fedit -file server.go -op move -line 100 -end 120 -after 200 -v

# Move a function block to before another function (content-matched)
fedit -file routes.go -op move -match "func OldHelper(" -end 45 -beforematch "func NewHelper(" -v

# Swap two nginx server blocks
fedit -file nginx.conf -op move -match "server {" -endmatch "# end server 1" -aftermatch "# end server 2" -v

# Cut once, scaffold 3 copies at destination
fedit -file main.go -op move -line 5 -end 12 -after 100 -times 3 -v

规则:

  • 目标位置不得与源范围重叠——fedit 会报告包含行号的精确错误。
  • -times N:剪切一次,粘贴 N 次。净变化量 = 代码块大小 × (N−1)。默认 times=1,净变化量为零。
  • 若要将代码块移动到其他文件,请使用 -dest(参见上文“跨文件复制、移动和替换”)。

copy——将一段行复制到新位置
# Copy a config block to after a section header
fedit -file values.yaml -op copy -line 50 -end 65 -aftermatch "# staging" -v

# Duplicate a test fixture 10 times for parameterised tests
fedit -file fixtures_test.go -op copy -match "func TestCase(" -end 30 -after 200 -times 10 -v

# Reorder Python classes (copy source before target; overlap is allowed)
fedit -file processor.py -op copy -match "class ModuleProcessor_15" -endmatch "class ModuleProcessor_16" -beforematch "class ModuleProcessor_13" -v

规则:

  • 快照语义:在任何写入之前只读取一次源范围。即使目标与源重叠,所有 N 个副本也都是原始内容的相同克隆。
  • 若要将代码块复制到其他文件,请使用 -dest(参见上文“跨文件复制、移动和替换”)。
  • 净变化量 = 代码块大小 × times。

delete——删除行
# Delete a single line
fedit -file hosts -op delete -line 12 -end 12 -v

# Delete a block (lines 40-55)
fedit -file config.yaml -op delete -line 40 -end 55 -v

write——创建或覆盖文件
# Create a new file
fedit -file /tmp/note.txt -op write -text "Deployment started" -v

# Write multi-line content from another file
fedit -file /etc/motd -op write -textfile new-motd.txt -v

map——文件结构概览
# Map a Go file — see all functions, types, imports
fedit -file main.go -op map -lang go

# Map a Dockerfile — see stages and instructions
fedit -file Dockerfile -op map -lang dockerfile

# Map a Makefile — see variables, targets, duplicates
fedit -file Makefile -op map -lang makefile

支持的映射语言(21 种)

语言检测到的关键结构
gopackage、imports、types、interfaces、functions
pythonimports、classes、functions、decorators
javascriptimports、exports、classes、functions、arrows
typescript(同 javascript)
cssimports、custom properties、selectors、@media、@keyframes;重复选择器按容器块计数,因此不同 @media 或 @keyframes 块中的同一选择器不算重复(自 v1.11.0 起)
rustuse、mod、struct、enum、trait、impl、fn、macro_rules!
javapackage、imports、classes、interfaces、enums、methods
csharpusing、namespace、classes、interfaces、records、properties
htmldoctype、head/body、headings、scripts、links、forms、ids
sqlCREATE、ALTER、DROP、INSERT、SELECT、indexes、triggers
yaml文档分隔符、顶层键
tomltables、array tables、顶层键
markdown标题(跳过围栏代码,因此不会将 shell 注释列为标题)、代码块、链接;重复标题(文本、级别和父级相同,且不区分大小写)会被标记(自 v1.11.0 起)
rubyrequires、modules、classes、methods、attributes
phpnamespace、use、classes、interfaces、traits、functions
dockerfileFROM 阶段、所有指令;重复的阶段名称(不区分大小写),以及同一阶段中重复的 CMD、ENTRYPOINT 或 HEALTHCHECK 会被标记(自 v1.11.0 起)
makefileincludes、variables、.PHONY、targets
json根类型、顶层键、嵌套深度、同一对象中的重复键、格式错误文件提示
csv行、列、标题列表、不规则行、重复标题列、空字段
hcl按类型划分的顶层块(terraform、provider、variable、locals、data、resource、module、output、moved、import、check)、重复块;terraform 和 tf 是别名(自 v1.11.0 起)
nix顶层属性块和简单属性、重复属性路径;仅限未缩进的行(自 v1.11.0 起)

大多数映射器都会检测重复项(函数、表、键、标题列、块、属性、选择器、标题、Dockerfile 阶段),并通过警告标记出来。


标志参考

标志描述
-file PATH目标文件(必填);用于 show/find/map 和 replaceall/insertafter/insertbefore/delete/replace/write 时,可使用逗号分隔的列表或 glob
-dest PATH,PATH跨文件复制、移动和替换:逗号分隔的目标文件列表;每个目标都需要一个锚点标志(-after、-before、-aftermatch、-beforematch、-afterblock、-beforeblock),或在 replace 中使用 -destblock(v1.9.0 起;MCP 自 v1.10.0 起支持)
-op OP要执行的操作(必填)
-line N起始行号(从 1 开始);N:+M=范围,-N=从末尾开始,-N:=最后 N 行,:=EOF
-end N结束行(用于范围);-N 表示从文件末尾开始计数
-text "s"内联文本内容
-textfile F从文件读取内容
-match "s"要搜索的子字符串
-nth N指定第几次出现(默认 1,-1 = 最后一次)
-lang LANGmap 和 -block 操作使用的语言
-v验证:显示编辑后受影响的行
-endmatch "s"内容锚点,用作范围的结束位置(show/replace/delete/move/copy)
-quiet成功时抑制输出,包括多文件和 -dest 执行时的逐文件报告(仍会显示跳过项和错误);退出代码表示结果(自 v1.11.0 起)
-texthex HEX以十六进制编码的 UTF-8 内容,绕过 shell 引号处理(v1.6.0)
-matchhex HEX, -endmatchhex HEX以十六进制编码的 -match 和 -endmatch 锚点,适用于包含双引号的锚点(v1.8.1)
-match-regex Preplaceall:带捕获组($1 $2)的正则表达式
-files GLOB对匹配 glob 的所有文件执行 replaceall(其他操作在 -file 中使用逗号分隔的列表或 glob)
-streamreplaceall 和 find 的大文件模式(v1.4.0)
-cleanfirst插入或写入前先截断文件(v1.6.0)
-rawshow:仅输出内容,不含行号或页脚,以便通过管道传给 fwencode(v1.7.0)
-xfind:仅输出行号;fields:不显示统计页脚(v1.6.0)
-col N, -delim CHARfields:列号(从 1 开始)和字段分隔符(默认为制表符)(v1.4.0)
-extract SPEC, -get REGEX, -wdelim CHARfind:子行提取(单词和字符切片)、正则预筛选和单词分隔符(v1.7.0;通过 MCP 使用时,无效的 get 正则表达式会报错,自 v1.11.0 起)
-block NAME定位命名函数、类或资源,而非使用行号(需要 -lang);块结束位置的判定会跳过字符串、多行字符串和注释,见下文“如何确定块的结束位置”(自 v1.11.0 起)
-after N, -before Nmove/copy 目标位置:在第 N 行之后或之前(0 = 文件开头)
-aftermatch TEXT, -beforematch TEXTmove/copy 目标位置:在首个包含 TEXT 的行之后或之前
-afterblock NAME, -beforeblock NAMEmove/copy 目标位置:在命名块之后或之前(需要 -lang);用于 insertafter 和 insertbefore 时会被拒绝,并报错提示使用 -block(自 v1.11.0 起)
-times Nmove/copy:粘贴 N 次(move 只剪切一次)
-destblock NAME仅用于 replace -dest:指定要在每个目标文件中覆盖的块(必填);缺少此项的目标会被跳过(v1.9.0 起;MCP 自 v1.10.0 起支持)
-sourcefile F, -sourceblock NAMEinsertafter/insertbefore:从另一个文件的命名块获取内容(需要 -lang;不能与 -text、-textfile 或 -texthex 同用)。-file 可为列表,且可以出现 Go 导入提示(两者均自 v1.11.0 起);这些标志本身自 v1.9.0 起提供,MCP 自 v1.10.0 起支持

如何查找代码块结束位置(自 v1.11.0 起)

使用 -block NAME(以及 -sourceblock、-destblock、-beforeblock、-afterblock)时,代码块范围由对应语言的扫描器确定。Python 和 Ruby 代码块不使用花括号计数,因此不受影响。Go、JS 和 TS、Rust、Java、C#、PHP、HCL 和 Nix 会统计花括号(Nix 还会统计方括号),但以下内容不会计入:

  • 字符串,包括多行字符串:反引号字符串(Go 原始字符串、JS 模板字面量)、C# 逐字字符串(@"...")和原始字符串(""")、Java 文本块、Rust 原始字符串(r"..."、r#"..."#)、HCL heredoc(<<EOT、<<-EOT)、PHP heredoc 和 nowdoc(<<<EOT、<<<'EOT',允许结束标记缩进)以及 Nix 缩进字符串(两个单引号)。在 Java 文本块中,反斜杠会转义下一个字符,因此转义后的 \""" 不会结束代码块。
  • Rust 字符字面量、生命周期和循环标签。
  • 注释:// 和 /* */(Rust 中可嵌套),以及 PHP、HCL 和 Nix 中的 #(在 PHP 中,#[ 表示属性的开始,不是注释)。Nix 没有 // 注释,因为 // 是它的更新运算符。

已知限制:包含未转义 /* 的 JS 正则表达式,以及在任何代码块之外开始的块注释。对于这类代码块,请先用 show 检查范围,再回退使用 -line。

真实示例

在部署脚本中修改 Nginx 配置
#!/bin/bash
# Update upstream server and reload

fedit -file /etc/nginx/conf.d/app.conf \
  -op replaceall \
  -match "server 10.0.1.50:8080" \
  -text "server 10.0.1.51:8080" -v

nginx -t && systemctl reload nginx
向 crontab 添加 cron 作业
fedit -file /etc/crontab \
  -op insertafter \
  -match "# Custom jobs" \
  -text "0 2 * * * root /opt/backup.sh" -v
批量更新多个文件中的版本
for f in Makefile config.yaml package.json; do
  fedit -file "$f" -op replaceall -match "1.1.0" -text "1.2.0" -v
done
编辑前检查 Dockerfile
fedit -file Dockerfile -op map -lang dockerfile
# See the structure, then surgically edit:
fedit -file Dockerfile -op insertafter -match "FROM alpine" -text "RUN apk add --no-cache curl" -v
从配置中移除代码块
# First, find the lines
fedit -file app.conf -op find -match "deprecated-feature"
# Output says lines 44-49, so:
fedit -file app.conf -op delete -line 44 -end 49 -v
PowerShell 工作流(Windows 系统管理员)
# Set an alias
$f = "C:\tools\fedit.exe"

# Find all TODO comments in Go code
& $f -file main.go -op find -match "TODO"

# Replace a config value
& $f -file config.toml -op replaceall -match 'debug = true' -text 'debug = false' -v

# Map a file to understand its structure
& $f -file main.go -op map -lang go

提示

  • 修改时始终使用 -v — 没有额外成本,还能避免盲目编辑
  • 替换前先查找 — 先获取准确的行号
  • 优先使用 insertafter/insertbefore,而不是 insert — 匹配比硬编码行号更可靠
  • 使用 -nth -1 定位匹配项的最后一次出现
  • 多行插入时使用 -textfile — 避免令人头疼的 shell 引号问题
  • 编辑不熟悉的文件前先使用 map — 先查看文件结构

大模型基准测试

当前大模型使用 fedit 与重写整个文件,哪种方式表现更好?

我们在 7 个真实编辑任务中测试了 Claude(Sonnet 4.6)、ChatGPT(GPT-4o) 和 Gemini(2.5 Pro),涉及行数为 565–1206 行的文件。每个模型针对每项任务测试两次:一次要求输出整个文件(“raw”),一次要求只输出 fedit 命令(“fedit”)。

结果

图例:PASS+ = 最优的单行解决方案 | PASS = 正确 | PASS* = 内容正确,但格式有瑕疵 | PARTIAL = 意图正确,但存在偏一错误或方案脆弱 | FAIL = 输出错误

测试文件任务Claude rawClaude feditGPT rawGPT feditGemini rawGemini fedit
T1processor_600.go(575 行)在指定的结构体方法后插入方法PASSPARTIALFAILPARTIALPASSFAIL
T2config_800.yaml(1059 行)替换 24 行的部署代码块PASSPASSFAILFAILPASSFAIL
T3styles_500.css(565 行)查找并删除 CSS 规则代码块PASSPASSPASS*FAILPASS*FAIL
T4system_1000.go(980 行)全局重命名(36 处)PASSPASS+FAILPASS+PASS*PASS+
T5analytics_700.py(682 行)三步链式操作(插入 + 删除 + 替换)PASSPARTIALFAILFAILPASSFAIL
T6dashboard_900.html(891 行)在第 3 个匹配按钮前插入(-nth)PASSPARTIALFAILPASS+PASSFAIL
T7engine_1200.go(1196 行)映射 + 在指定方法后插入PASSPASSFAILFAILPASSFAIL
核心数据
模型原始模式Fedit 模式
Claude7/7 PASS4 PASS,3 PARTIAL
ChatGPT1/7 PASS*2 PASS+,1 PARTIAL,4 FAIL
Gemini7/7 PASS*1 PASS+,6 FAIL
主要发现

1. ChatGPT 无法可靠地输出大型文件。 7 项原始测试中有 6 项输出被截断。最明显的失败出现在 T6:它在原本有效的 HTML 中插入了字面占位行 [... TRUNCATED FOR BREVITY ...]——这是一种独特且危险的失败模式:输出看起来结构完整,却包含占位字符串。T7 的输出则从 1206 行截断到 166 行。

2. 大模型会幻觉行号,而且链条越长,情况越严重。 Gemini 的行号错误在整个测试套件中不断增大:T2(单次替换)偏差 36–56 行,T3 偏差 45 行,到了 T5(三步链)则达到 73 行。ChatGPT 在 T5 中也出现了类似的偏差。只有 Claude 能始终如一地产生可运行的带行号命令。

3. 基于内容匹配的操作不会受到这类幻觉的影响。 Gemini 在所有需要行号的 fedit 测试中都失败了(T1、T2、T3、T5、T6、T7),但通过了使用 replaceall 进行内容匹配的 T4。同一个模型、相同的任务复杂度,可靠性却大不相同。瓶颈在于计数,而非理解。

4. 对函数声明使用 insertafter 会匹配函数的起始行。 模型反复尝试使用 insertafter -match "func MyFunc()",想在函数结束后插入内容。这符合 fedit 的预期行为(它会在匹配行之后插入内容),但新代码会被插入函数体内。解决方法:在下一个结构元素之前插入。已在 T1 和 T7 中确认。

5. 连 Claude 也会弄错行号方向。 T6:Claude 使用 insert -line 30,想在第 30 行的第 3 个按钮之前添加横幅。但 insert -line N 会在第 N 行之后添加内容。正确命令是 insertbefore -match "View Details" -nth 3。即便是最强的模型,在选择行号进行单步操作时,大约每 6 次也会有 1 次弄错方向。

6. -match 只支持单行匹配——这反而是优点。 ChatGPT(T3 fedit)试图将一个多行 CSS 代码块作为 -match 参数传入,并使用字面 \n 转义符。fedit 不会在 -match 中解释转义符,且只匹配单行。这让操作保持安全(没有匹配项 → 不进行编辑),避免使用容易发生不匹配的脆弱多行模式。

7. Markdown 渲染是隐藏的干扰因素。 ChatGPT 和 Gemini 的输出在聊天界面渲染时,会将 __name__ 变成 name、将 __init__ 变成 **init**,并去掉 CSS/Python 缩进。直接下载的底层文件则正确无误。这会影响复制粘贴工作流,但不会影响 API 集成。为获得最佳效果,请使用模型的“复制代码”按钮或下载链接,切勿从渲染后的输出中选择并复制。

8. 三种不同的模型对 T4 得出了相同的单行命令。 Claude、ChatGPT 和 Gemini 都独立生成了 fedit -op replaceall -match "FetchUser" -text "GetAccount"。 当正确的工具一目了然时,模型就会选用它。fedit 的设计让基于内容的编辑显而易见地适合使用该工具。

面向 LLM 驱动工作流的建议

根据这些结果,从 LLM 生成 fedit 命令时,优先使用按内容匹配的操作,而非按行号操作:

  • 使用 insertbefore -match "next anchor",而不是 insert -line N
  • 使用 replaceall -match "old" -text "new",而不是 replace -line N -end M
  • 在执行任何按行号操作之前,使用 find -match 和 show 确认行号
  • 仅在 MCP 连接的 LLM 刚刚运行 fedit_find 或 fedit_show,并已在上下文中核实行号时,才使用按行号操作
  • 使用 -block / -lang 定位命名函数和结构体,无需行号

为获得最佳效果,请为 LLM 提供 fedit 的 MCP 连接(参见 MCP 服务模式),使其在修改文件前能够运行 fedit_find 和 fedit_show。这样可以彻底消除行号幻觉;在 Claude 可以使用 recon 时,它始终选择了这种方式。

方法
  • 每次测试都在一个新的聊天中进行,并上传原始文件
  • 所有模型使用相同的提示词;只有“raw”和“fedit”的表述不同
  • 原样保存输出,然后使用 Compare-Object 与基准结果进行差异比较
  • 通过在本地运行 fedit 命令并重新读取输出,验证基准结果
  • 所有 7 个 raw 输出均通过 Compare-Object 空结果检查(与基准结果逐字节一致):Claude T2、T3、T4、T5、T6、T7 + Gemini T5、T6、T7
  • PASS* 表示聊天界面渲染时去除了缩进,导致字节差异,但内容结构正确(通过对保存的文件重新运行 fedit 操作进行了验证)
测试语料

7 个合成文件(565–1206 行),涵盖 Go、YAML、CSS、Python 和 HTML。 完整结果、基准文件、测试提示词和 7 个文件组成的语料: amalexhandler.com/fedit#benchmark。

MCP 服务模式

fedit 内置了 Model Context Protocol (MCP) 服务,因此 AI 编码助手可以使用 fedit 精确编辑文件。

fedit mcp

这会在 stdin/stdout 上启动 JSON-RPC 2.0 服务。该服务提供全部 14 种编辑操作作为 MCP 工具:

工具描述
fedit_show显示文件内容(全部内容或指定行范围);file 支持逗号分隔列表或 glob
fedit_insert在指定行号后插入内容
fedit_delete删除一行或多行;file 支持逗号分隔列表或 glob,且可与 match 配合使用
fedit_replace替换行范围或命名块;file 支持逗号分隔列表或 glob,且可与 match 配合使用;dest + destblock 可覆盖其他文件中的命名块
fedit_replaceall全局查找并替换;match_regex 用于正则表达式,textfile 用于替换文本(两者均自 v1.11.0 起支持);file 支持逗号分隔列表或 glob
fedit_write创建或覆盖文件
fedit_writeraw创建或覆盖文件,不展开转义序列(反斜杠按字面处理)
fedit_map结构概览(支持 21 种语言;未指定时根据扩展名检测 lang);file 支持逗号分隔列表或 glob
fedit_find查找与子字符串匹配的行;file 支持逗号分隔列表或 glob
fedit_insertafter在匹配行或命名块后插入;sourcefile + sourceblock + lang 可从其他文件插入代码块(自 v1.11.0 起提供 Go 导入提示);file 支持逗号分隔列表或 glob
fedit_insertbefore在匹配行或命名块前插入;sourcefile + sourceblock + lang 可从其他文件插入代码块(自 v1.11.0 起提供 Go 导入提示);file 支持逗号分隔列表或 glob
fedit_move将行范围移至新位置;拒绝目标范围重叠;dest 可将代码块移至其他文件
fedit_copy复制行范围;采用快照语义,允许重叠,支持 -times N;dest 可将代码块复制到其他文件
fedit_fields从 CSV/TSV/分隔符分隔文件中提取第 N 列(-col N -delim CHAR);始终采用流式处理
使用 Claude Desktop 进行配置

添加到你的 claude_desktop_config.json:

{
  "mcpServers": {
    "fedit": {
      "command": "fedit",
      "args": ["mcp"]
    }
  }
}

完整的设置指南(Windows 路径、故障排除、Cursor/Cline/Windsurf 代码片段):CLAUDE_DESKTOP.md

使用 Cursor 进行配置

添加到项目根目录下的 .cursor/mcp.json:

{
  "mcpServers": {
    "fedit": {
      "command": "fedit",
      "args": ["mcp"]
    }
  }
}
为什么使用 MCP?

不使用 fedit 时,大模型会重写整个文件,浪费 token 并引入偏差。将 fedit 作为 MCP 工具后,模型会调用 fedit_map 查看结构,调用 fedit_find 定位目标,再调用 fedit_replace 或 fedit_insertafter 进行精准编辑。每次修改都会返回统计信息(行数增减、耗时),以便模型验证操作结果。


常见问题

如果团队有完善的 PR 审核流程,还需要 fedit 吗?

对于每次都会审查差异的人工作业,可能不需要。fedit 在两种特定情况下尤其有用:

  1. 重写长配置文件。 当大模型为了修改一个键而重新生成 600 行的 values.yaml 时,差异在技术上可以审查,但实际上没人会滚动到第 412 行去确认其他内容没有变化。使用 fedit,修改 N 行时,差异恰好只有 N 行。

  2. 中间没有人工介入的 Agent 循环。 如果你以半自主方式运行 Agent(问题 → 分支 → 创建 PR,最后由你审核),为 Agent 提供精准工具(fedit_replace、fedit_insertafter),而不是让它“重写整个文件”,就能显著缩小审核范围。

如果你的工作流已经能发现这些问题,那么命令行工具就有些大材小用了。MCP 服务仍可作为 Agent 的基础工具。

fedit 是否支持所有操作的流式处理?

目前,replaceall 和 find 支持 -stream 模式。move 和 copy 需要将整个文件载入内存,因为它们需要读取源和目标范围。map 需要进行结构分析。fields 始终采用流式处理。

fedit 能处理的最大文件有多大?

在默认(内存)模式下,大小受可用 RAM 限制,通常处理数百 MB 的文件没有问题。使用 -stream 时则没有大小限制——fedit 每次处理一行,并使用 10 MB 的逐行缓冲区。

fedit 支持 Terraform / HCL 吗?

支持,从 v1.5.0 开始。使用 -lang hcl(或 -lang tf/terraform)搭配 -block、-beforeblock、-afterblock,即可按名称移动和复制 Terraform 块。支持全部 11 种顶层块类型。从 v1.11.0 开始,map 操作支持 HCL/Terraform 和 Nix:对 .tf、.hcl 或 .nix 文件执行 fedit -op map(或搭配 -lang hcl、terraform、tf 或 nix),即可列出顶层块(HCL)或属性(Nix)并标记重复项;fedit_map 也可通过 MCP 执行相同操作。

fedit 会保留注释和格式吗?

会,这是其设计原则。fedit 不会解析并重新格式化,而是以行为粒度直接处理原始文本字节。

如果你执行 replace -line 47 -end 49,第 1–46 行和第 50 行至文件末尾都将逐字节保持不变:注释、尾随空格、混合缩进、BOM 标记都会原样保留。这也是 fedit 按行定位而非基于 AST 的主要原因:AST 往返会丢失太多内容(尾随逗号、注释位置、键顺序、空行间距)。

这对 nginx 配置(内联注释会说明指令存在的原因)、Ansible playbook(YAML 锚点和合并键),以及任何人工选择的格式具有语义重要性的文件来说尤其关键。

MCP 服务是专为 Claude 还是 Cursor 设计的?

两者都不是——它面向开放的 MCP 规范。它通过标准输入/输出使用纯 JSON-RPC 2.0,并遵循公开的 Model Context Protocol 架构,不包含任何厂商专属内容。

已在 Claude Desktop 中测试;应该也能与任何符合规范的 MCP 客户端配合使用(Cursor、Continue、Cline、自定义集成)。工具定义和 JSON Schema 位于 mcp.go,你可以在接入前查看其接口范围。

如果目标字符串在文件中出现多次,该怎么办?

使用 -nth N 标志:

  • -nth 1(默认)——第一个匹配项
  • -nth 2 ——第二个匹配项
  • -nth -1 ——最后一个匹配项
  • 任意正整数都可指定对应位置的匹配项

示例:定位第三个 http.Redirect 调用:

fedit -file router.go -op insertbefore -match "http.Redirect" -nth 3 -textfile patch.txt -v

如果不确定有多少个匹配项,请先运行 find——它会列出每个匹配项及其上下文,方便你在修改前确认目标:

fedit -file router.go -op find -match "http.Redirect"
链式执行命令会导致行号偏移吗?

不会。每次调用 fedit 时都会重新读取文件,因此行号始终反映磁盘上的当前状态。你可以使用 ;(PowerShell)或 &&(bash)安全地链式执行操作:

fedit -op replaceall -file f.conf -match "old.com" -text "new.com" -v ; fedit -op replace -file f.conf -line 42 -end 42 -text "fixed" -v

这样做的代价是每次操作都会读取一次磁盘,对于编辑工作流来说,这点开销可以忽略不计。

如果希望从设计上避免行号偏移,优先使用基于内容的定位方式(insertafter / insertbefore / replaceall 配合 -match),而不是行号——无论之前执行了什么操作,这些方式都会在每次调用时重新确定目标。

许可证

MIT——参见 LICENSE


由 Amalex 构建——Amalex Handler 的开发者,这是一款通用文件传输工具。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。