首页
/ llama.cpp 的 Bash 命令行补全:`--completion-bash` 的用法、生成逻辑与源码原理

llama.cpp 的 Bash 命令行补全:`--completion-bash` 的用法、生成逻辑与源码原理

2026-09-06 12:09:57作者:魏献源Searcher

llama.cpp 提供了数十个命令行工具(llama-cli、llama-server、llama-quantize 等),其参数众多且命名冗长,逐个记忆成本很高。本文以 docs/completions.md 为主线,完整讲解如何通过 --completion-bash 参数为一键式启用 Bash 命令补全,并结合 common/arg.cpp 中的实现源码,剖析该补全脚本是如何被动态生成、如何按文件扩展名过滤候选值,以及哪些可执行文件被纳入了补全范围,帮助你在日常运行、量化、部署推理服务时大幅减少手敲参数的错误。

一、功能概述:命令行补全在 llama.cpp 中的定位

llama.cpp 的核心功能是大语言模型推理,而围绕推理主流程,仓库提供了批量推理、基准测试、嵌入提取、量化、服务端等大量配套工具。这些工具共享同一套由 common 库实现的参数解析体系,因此也共享一套统一的补全能力:

  • 补全仅在 Bash 环境下可用,官方文档的表述是"Command-line completion is available for some environments",目前仓库内置的只有 Bash 脚本生成方案;
  • 补全能力不依赖任何外部插件或单独安装的 shell 扩展,而是由二进制自身通过 --completion-bash 参数直接打印出一段可 source 的脚本;
  • 每个工具文档的参数表中都能看到该参数,例如 tools/cli/README.mdtools/server/README.md 的选项表里均列有 --completion-bash,其描述均为 "print source-able bash completion script for llama.cpp"(打印一份可 source 的 Bash 补全脚本)。

需要注意区分:llama-completion 本身也是一个可执行文件名(负责文本续写任务的工具),它与 --completion-bash 这个参数名只共享"completion"一词,功能上毫无关系。

二、启用 Bash 补全:官方文档的完整步骤

2.1 生成并加载补全脚本

按照 docs/completions.md 的原始步骤,只需两行命令:

$ build/bin/llama-cli --completion-bash > ~/.llama-completion.bash
$ source ~/.llama-completion.bash

第一步执行的是 llama-cli(编译产物位于 build/bin/ 目录),由于 --completion-bash 的语义是"把脚本打印到标准输出后退出",因此重定向到 ~/.llama-completion.bash 即可得到完整脚本文件;第二步用 source 将脚本加载进当前 shell,补全函数随即生效。

之后在命令行输入参数名前缀并按 Tab,Bash 就会列出候选项,例如输入 llama-cli --mo 再按 Tab 可补全 --model,输入 -l 可补全相关短参数。

2.2 让补全随 shell 自动加载

source 只对当前终端会话有效。官方文档给出了一次性写入 rc 文件的做法,使之后每次打开的 Bash 终端自动具备补全能力:

$ echo "source ~/.llama-completion.bash" >> ~/.bashrc

也可以把该行加入 ~/.bash_profile。写入后新开一个终端即可验证:输入 llama-server -- 按 Tab 应能列出服务端参数。

三、--completion-bash 的源码实现剖析

3.1 参数注册与分发路径

--completion-bash 作为通用参数注册在参数解析库中,见 common/arg.cpp

add_opt(common_arg(
    {"--completion-bash"},
    "print source-able bash completion script for llama.cpp",
    [](common_params & params) {
        params.completion = true;
    }
));

该参数属于通用参数(common options),因此所有链接了 common 解析器的工具都具备它。参数被识别后的分发逻辑在 common/arg.cpp

if (ctx_arg.params.completion) {
    common_params_print_completion(ctx_arg);
    exit(0);
}

这里有两个值得注意的实现细节:

  1. 先完整解析参数,再判断是否需要打印补全。也就是说解析流程会先走一遍常规路径,确认参数合法后才进入补全分支,保证脚本内容与当前二进制实际注册的参数完全一致;
  2. 打印脚本后直接 exit(0),不会加载模型、初始化后端或做任何推理相关工作。整个动作只是向 stdout 输出纯文本,这也是重定向到文件可行、且执行瞬间完成的原因。

3.2 生成的补全脚本结构

核心生成函数是 common_params_print_completion,位于 common/arg.cpp。它并非输出一份静态文本,而是根据当前二进制实际注册的所有参数动态拼装一份 Bash 函数。生成逻辑分三步:

第一步:按类别归集参数。 遍历上下文中注册的全部选项,按四个维度分组(common/arg.cpp):

  • is_sampling 的采样类参数;
  • is_spec 的投机解码类参数;
  • in_example(ctx_arg.ex) 命中的、当前示例(即当前可执行文件)专用的参数;
  • 其余的通用参数。

这四个分组与 --help 打印 usage 时的分组逻辑一致。由于"当前示例专用参数"这一维度依赖于你运行的是哪个二进制,llama-cli --completion-bash 生成的脚本会包含 CLI 专属参数,而 llama-server --completion-bash 生成的则包含服务端专属参数——参数列表因工具而异,这是动态生成的直接结果。

第二步:拼装 _llama_completions 函数。 生成的函数遵循 Bash 标准补全协议,其骨架(由源码逐行 printf 输出)如下:

_llama_completions() {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    opts="...(所有已注册参数的短名与长名)..."

    case "$prev" in
        --model|-m)
            COMPREPLY=( $(compgen -f -X '!*.gguf' -- "$cur") $(compgen -d -- "$cur") )
            return 0
            ;;
        --grammar-file)
            COMPREPLY=( $(compgen -f -X '!*.gbnf' -- "$cur") $(compgen -d -- "$cur") )
            return 0
            ;;
        --chat-template-file)
            COMPREPLY=( $(compgen -f -X '!*.jinja' -- "$cur") $(compgen -d -- "$cur") )
            return 0
            ;;
        *)
            COMPREPLY=( $(compgen -W "${opts}" -- "$cur") )
            return 0
            ;;
    esac
}

其中:

  • cur / prev 分别取自 COMP_WORDS 数组的当前词与前一词,是 Bash 程序化补全的通用写法;
  • opts 字符串由所有参数的所有写法拼成——源码对每个 common_arg 会遍历其 args 列表逐个输出(common/arg.cpp),因此短选项(如 -m)和长选项(如 --model)都在候选之列;
  • 默认分支 *)compgen -W "${opts}" 按已输入前缀过滤参数名,实现"输入 -- 后 Tab 列出全部参数"的基本体验。

第三步:按文件扩展名过滤的参数级补全。 上面 case "$prev" 部分是脚本中最有价值的细节——它识别"前一个词是哪个参数",从而对参数取值做类型感知的补全:

前一个词(待填取值的参数) 补全策略 含义
--model-m compgen -f -X '!*.gguf' 只列出 *.gguf 文件,同时保留目录补全
--grammar-file compgen -f -X '!*.gbnf' 只列出 *.gbnf 语法文件
--chat-template-file compgen -f -X '!*.jinja' 只列出 *.jinja 聊天模板文件

这与 llama.cpp 的资源约定完全对应:模型权重是 .gguf 格式、约束语法是 .gbnf(仓库的 grammars/ 目录存放的即此类文件)、聊天模板是 .jinja(对应 models/templates/ 目录)。因此补全后你输入 --model ./m + Tab,弹出的正是本地 gguf 模型列表,而不是满屏无关文件。

3.3 哪些可执行文件被纳入补全范围

生成的脚本末尾会为一批可执行文件注册同一个补全函数。源码中维护了一个硬编码的名单(common/arg.cpp):

std::set<std::string> executables = {
    "llama-batched",
    "llama-bench",
    "llama-cli",
    "llama-completion",
    "llama-server",
    "llama-quantize",
    "llama-imatrix",
    "llama-mtmd-cli",
    "llama-tts",
    // ... 其余工具
};

for (const auto& exe : executables) {
    printf("complete -F _llama_completions %s\n", exe.c_str());
}

当前名单共包含 45 个可执行文件,覆盖推理(llama-clillama-completionllama-parallel)、服务端(llama-server)、评测(llama-benchllama-batched-benchllama-perplexity)、量化与矩阵(llama-quantizellama-imatrixllama-cvector-generator)、状态管理(llama-lookup 系列、llama-save-load-state)、多模态(llama-mtmd-cli)等全部主要工具类别。

由于所有工具注册到的是同一个 _llama_completions 函数,一次 source 即可让名单内全部命令共享补全能力,无需为每个工具分别配置。也正因为名单是在源码中硬编码的,从源码结构看,后续新增的命令行工具需要同步加入该集合才能被生成脚本覆盖——如果某个工具名按 Tab 无反应,可优先检查它是否在该名单内。

四、使用注意事项与适用前提

  1. 仅支持 Bash。文档明确限定了适用范围,仓库中没有提供 zsh、fish 等 shell 的对应生成逻辑;zsh 用户可自行基于同一脚本改造(例如用 compdef 注册 _llama_completions),但这属于文档范围之外的自行为。
  2. 脚本内容与二进制版本绑定。参数列表来自二进制的运行时注册表,llama.cpp 迭代较快,参数增删频繁。升级 build/ 下的构建产物后,建议重新执行一次 --completion-bash 重定向,避免旧脚本中残留已删除的参数或遗漏新参数。
  3. 不同二进制生成的选项集不同。如 3.2 节所述,in_example 过滤使脚本携带了生成它的工具专属参数。文档以 llama-cli 为例只是惯例选择;若你主要使用 llama-server,用 build/bin/llama-server --completion-bash 生成脚本可以让服务端的专属参数也进入候选列表。
  4. 补全不校验取值合法性--model 之后只按扩展名过滤文件,文件存在但不一定是当前架构支持的模型,加载阶段的错误检查依然由推理引擎负责。

五、延伸阅读

  • 原始文档:docs/completions.md(本文主线,Bash 补全的最小操作指南);
  • 参数实现:common/arg.cppcommon_params_print_completion(L1004–L1114)、--completion-bash 注册(L1467–L1473)与分发逻辑(L1302–L1305);
  • 各工具的参数表:tools/cli/README.mdtools/server/README.md,可对照确认某个参数是否会在补全候选中出现;
  • 补全过滤所依赖的资源格式:GBNF 语法示例见 grammars/ 目录,Jinja 聊天模板见 models/templates/ 目录。
登录后查看全文
热门项目推荐
相关项目推荐