Gemini CLI run_shell_command 工具详解:执行机制、交互式 Shell 与安全限制
run_shell_command 是 Gemini CLI 中让模型直接与你的系统 Shell 交互的核心工具,也是 Agent 能力超出"简单文件编辑"的关键机制。本篇基于 shell 工具文档 并结合 shell.ts、shellExecutionService.ts 等源码,完整讲解该工具的执行方式、参数与返回值、settings.json 配置项、交互式命令支持,以及通过 tools.core / Policy Engine 限制可执行命令的安全模型。读完你可以准确配置交互式 Shell、pager 与超时行为,并能编写针对 shell 命令的白名单/黑名单策略。
工具定位:Agent 与操作系统之间的桥梁
在 Gemini CLI 的工具体系中,run_shell_command 允许模型在你的系统 Shell 上直接执行命令。它是 Agent 与外部环境交互的主要机制:运行构建脚本、执行测试套件、管理版本控制、安装依赖、启动开发服务器与后台监听进程,都依赖这个工具。
工具入口定义在 ShellTool / ShellToolInvocation:ShellTool 负责参数校验与工具声明(schema),每次调用都会生成一个 ShellToolInvocation 实例,负责确认交互、策略更新与实际执行。
技术参考:Shell 选择与执行方式
不同平台的底层 Shell
文档给出的规则是:Windows 上命令通过 powershell.exe -NoProfile -Command 执行,其他平台通过 bash -c 执行。
从源码看,这一行为由 getShellConfiguration() 决定:
- 非 Windows 平台固定返回
{ executable: 'bash', argsPrefix: ['-c'], shell: 'bash' }; - Windows 平台会优先在 PATH 中查找 PowerShell Core(
pwsh),找不到时回退到powershell.exe,参数前缀为-NoProfile -NonInteractive -Command,比文档描述的-NoProfile -Command多了一个-NonInteractive,即跳过交互式提示,保证自动化执行不会挂起; - 特殊情况下(Windows 严格沙箱且无网络访问),执行器会被替换为
cmd.exe /c(见 shellExecutionService.ts)。
参数(Arguments)
模型调用 run_shell_command 时可传入的参数如下(与 ShellToolParams 一致):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
command |
string | 是 | 要执行的精确 shell 命令 |
description |
string | 否 | 展示给用户用于确认执行的简短描述;命令较短(≤150 码点)时界面直接显示命令本身,较长时显示 description |
dir_path |
string | 否 | 命令运行的目录,可以是绝对路径或相对 workspace 根目录的路径;会被 validatePathAccess 校验,确保位于 workspace 内 |
is_background |
boolean | 否 | 是否在启动后立即把进程转入后台 |
执行前,工具会先做参数校验:命令不能为空,dir_path 解析后必须落在工作区内,否则直接返回 PATH_NOT_IN_WORKSPACE 错误(见 validateToolParamValues)。
返回值与内部执行流程
返回字段
工具返回一个包含以下信息的结构(源码中组装为 llmContent 文本,见 shell.ts#L799-L831):
Command:执行的命令字符串;Directory:执行路径(由dir_path解析或当前工作目录);Stdout/Stderr:输出流;Exit Code:进程返回码(非 0 时同时标记isError: true);Background PIDs:启动的后台进程 PID 列表;- 另外还有
Signal(终止信号)与Process Group PGID等补充字段。
命令替换注入检测
在执行前,源码会调用 detectCommandSubstitution() 检查命令中是否存在 $()、反引号、<() / >() 等命令替换语法,检测到则直接拒绝执行并返回 "Command injection detected" 提示;在 PowerShell 场景下同样拦截 @() 与 $() 子表达式。
后台进程 PID 的捕获原理
Background PIDs 字段的实现值得一看。在 POSIX 平台,wrapCommandForBackgroundPIDs() 会把原始命令包进一个子 shell:
_bgpids_file=<临时文件>
(
trap 'jobs -p > "$_bgpids_file"' EXIT
<你的原始命令>
)
__code=$?
exit $__code
利用 EXIT trap 在子 shell 退出时把 jobs -p 的输出写入临时文件,从而在父进程层面拿到所有后台子进程的 PID(过滤掉与主进程相同的 PID 后写入返回值的 Background PIDs)。Windows 上由于不支持 POSIX jobs 语义,命令原样执行,不做该包装。当 is_background: true 时,工具在短暂延迟(默认 200ms,BACKGROUND_DELAY_MS)后调用 ShellExecutionService.background() 把进程转入后台并立即返回 Command is running in background. PID: <pid>。
输出节流与超时
从源码常量可以看到两处关键防护:
OUTPUT_UPDATE_INTERVAL_MS = 1000:实时输出最多每秒刷新一次;LIVE_OUTPUT_MAX_BUFFER_CHARS = 100_000:实时输出缓冲上限约 100K 字符,超出后截断并保留尾部(注意按 UTF-16 代理对边界对齐,避免截坏 emoji)。
不活动超时由 inactivityTimeout 配置驱动:每次收到任何输出事件都会 resetTimeout() 重置计时器,超时后自动终止命令,并提示 Command was automatically cancelled because it exceeded the timeout of X minutes without output。
通过 Policy Engine 的简写字段限制 Shell 命令
Policy Engine 参考 提供了两个专门针对 shell 命令的便捷字段:
commandPrefix:当command参数以给定字符串开头时匹配;commandRegex:当command参数匹配给定正则表达式时匹配。
需要强调:这两个字段不是 run_shell_command 本身的参数,而是策略 TOML 文件中的语法糖,等价于组合 toolName = "run_shell_command" 与 argsPattern。在 policy-engine.md#special-syntax-for-run_shell_command 中可以看到示例规则,例如:
[[policy]]
toolName = "run_shell_command"
commandPrefix = "git"
规则级注意点:同一条规则中不能同时使用 commandPrefix 和 commandRegex。
配置项:settings.json 中的 tools.shell
run_shell_command 的行为可通过修改 settings.json 或使用 Gemini CLI 的 /settings 命令配置。完整字段定义在 settingsSchema.ts 的 shell 配置节。
启用交互式命令(enableInteractiveShell)
设置 tools.shell.enableInteractiveShell 为 true 后,命令执行改用 node-pty 伪终端,从而支持交互式会话;若 node-pty 不可用,会回退到 child_process 实现,后者不支持交互式命令。当前 schema 中该默认值即为 true(requiresRestart: true,修改后需重启生效)。
{
"tools": {
"shell": {
"enableInteractiveShell": true
}
}
}
执行路径选择逻辑在 ShellExecutionService.execute():shouldUseNodePty 为真时优先 executeWithPty,失败或不可用时落到 childProcessFallback。
显示彩色输出(showColor)
tools.shell.showColor 为 true 时,shell 输出中保留 ANSI 颜色。该设置仅在 enableInteractiveShell 启用时生效,schema 默认值为 true。
{
"tools": {
"shell": {
"showColor": true
}
}
}
设置 pager(pager)
tools.shell.pager 指定 shell 输出使用的分页器,默认是 cat(仅在交互式 Shell 启用时生效)。源码在 prepareExecution() 中将其注入子进程环境:
{
"tools": {
"shell": {
"pager": "less"
}
}
}
其他相关配置
从 schema 看,tools.shell 下还有两个对 shell 行为影响直接的选项:
inactivityTimeout(number):命令无输出时允许的最大秒数,超时则终止进程,默认300(5 分钟);backgroundCompletionBehavior(enum,默认silent):控制后台 shell 命令完成后的行为——silent静默结束、inject自动把输出返回给 Agent、notify在聊天中显示简短提示;enableShellOutputEfficiency(boolean,默认true):shell 输出效率优化开关。
交互式命令
通过集成伪终端(pty),run_shell_command 支持需要实时用户输入的命令,例如文本编辑器(vim、nano)、终端 UI(htop)、交互式版本控制操作(git rebase -i)。
使用方式:当交互式命令运行时,可以在 Gemini CLI 中向其发送输入——按 Tab 聚焦到交互式 shell,终端输出(包括复杂 TUI)都会被正确渲染。
环境变量
run_shell_command 执行命令时,会在子进程环境中注入 GEMINI_CLI=1,脚本或工具据此可以判断自己是否运行在 Gemini CLI 内部。这一点在源码中由常量 GEMINI_CLI_IDENTIFICATION_ENV_VAR 定义(值为 '1')。
此外,从 prepareExecution() 可以看到一层"环境净化与 git 安全加固",这些对任何在 CLI 中执行的 git 命令都成立:
- 基础环境注入:
TERM=xterm-256color,PAGER/GIT_PAGER默认cat; - git 全局与系统配置指向
/dev/null(GIT_CONFIG_GLOBAL、GIT_CONFIG_SYSTEM、GIT_CONFIG_NOSYSTEM=1),避免读取用户级 git 配置; - 通过
GIT_CONFIG_KEY_*/GIT_CONFIG_VALUE_*显式禁用credential.helper、core.hooksPath、core.sshCommand、core.fsmonitor、core.editor、diff.external等,防止子命令通过 hooks / ssh 执行意外代码; - 禁用交互凭证提示:
GIT_TERMINAL_PROMPT=0、GIT_ASKPASS、SSH_ASKPASS置空、GH_PROMPT_DISABLED=1,以及清理DISPLAY、DBUS_SESSION_BUS_ADDRESS等会话变量; - 其余环境变量经过
sanitizeEnvironment脱敏,仅允许白名单变量(以及GIT_CONFIG_*系列)穿透。
命令限制(tools.core / tools.exclude)
警告:
tools.core是所有内置工具的 allowlist,而不是仅针对 shell 命令。一旦给tools.core设置了任何值,只有被明确列出的工具会被启用,包括read_file、write_file、glob、grep_search、list_directory、replace等全部内置工具。
可以通过配置文件中 tools.core 与 tools.exclude 限制 run_shell_command 可执行的命令:
tools.core:在tools.core列表中以run_shell_command(<command>)格式添加条目来限定允许的命令。例如"tools": {"core": ["run_shell_command(git)"]}只允许git命令;列入通用的run_shell_command(不带括号参数)则相当于通配符,允许所有未被显式阻止的命令。tools.exclude【已废弃】:现在应改用 Policy Engine 阻止特定命令。历史上该设置支持以run_shell_command(<command>)格式添加条目,例如"tools": {"exclude": ["run_shell_command(rm)"]}会阻止rm命令。
验证逻辑被设计得安全且灵活:
- 禁用命令链:工具会自动拆分用
&&、||或;串联的命令,对每一段分别校验。链中任何一段不被允许,整个命令都会被阻止。 - 前缀匹配:例如允许
git后,可以运行git status、git log。 - 黑名单优先:
tools.exclude列表总是先检查。命令若匹配被阻止的前缀即被拒绝,即使它同时匹配tools.core中的允许前缀。
命令限制示例
只允许特定命令前缀:仅允许 git 和 npm,阻止其他一切命令:
{
"tools": {
"core": ["run_shell_command(git)", "run_shell_command(npm)"]
}
}
git status:允许npm install:允许ls -l:阻止
阻止特定命令前缀:阻止 rm,允许其他所有命令:
{
"tools": {
"core": ["run_shell_command"],
"exclude": ["run_shell_command(rm)"]
}
}
rm -rf /:阻止git status:允许npm install:允许
黑名单优先:若某命令前缀同时出现在 tools.core 与 tools.exclude 中,将被阻止。
使用与安全提示
典型用例:
- 运行构建脚本与测试套件;
- 初始化或管理版本控制系统;
- 安装项目依赖;
- 启动开发服务器或后台监听进程。
文档中的注意事项(结合源码印证):
- 安全:执行命令时要格外谨慎,尤其是由用户输入构造的命令,防止注入类安全漏洞——源码层的命令替换检测正是这道防线之一;
- 错误处理:检查
Stderr、Error与Exit Code字段判断命令是否成功; - 后台进程:命令以
&放入后台时,工具会立即返回,进程继续在后台运行,Background PIDs字段包含其 PID。
延伸阅读
- Shell 命令实战教程:更多实操示例;
- 沙箱(Sandboxing):了解如何隔离命令执行,配合
sandboxManager对命令做权限收敛; - Policy Engine 参考:编写
run_shell_command专属策略规则的完整语法。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00