首页
/ Gemini CLI run_shell_command 工具详解:执行机制、交互式 Shell 与安全限制

Gemini CLI run_shell_command 工具详解:执行机制、交互式 Shell 与安全限制

2026-09-06 14:05:50作者:凌朦慧Richard

run_shell_command 是 Gemini CLI 中让模型直接与你的系统 Shell 交互的核心工具,也是 Agent 能力超出"简单文件编辑"的关键机制。本篇基于 shell 工具文档 并结合 shell.tsshellExecutionService.ts 等源码,完整讲解该工具的执行方式、参数与返回值、settings.json 配置项、交互式命令支持,以及通过 tools.core / Policy Engine 限制可执行命令的安全模型。读完你可以准确配置交互式 Shell、pager 与超时行为,并能编写针对 shell 命令的白名单/黑名单策略。

工具定位:Agent 与操作系统之间的桥梁

在 Gemini CLI 的工具体系中,run_shell_command 允许模型在你的系统 Shell 上直接执行命令。它是 Agent 与外部环境交互的主要机制:运行构建脚本、执行测试套件、管理版本控制、安装依赖、启动开发服务器与后台监听进程,都依赖这个工具。

工具入口定义在 ShellTool / ShellToolInvocationShellTool 负责参数校验与工具声明(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"

规则级注意点:同一条规则中不能同时使用 commandPrefixcommandRegex

配置项:settings.json 中的 tools.shell

run_shell_command 的行为可通过修改 settings.json 或使用 Gemini CLI 的 /settings 命令配置。完整字段定义在 settingsSchema.ts 的 shell 配置节

启用交互式命令(enableInteractiveShell)

设置 tools.shell.enableInteractiveShelltrue 后,命令执行改用 node-pty 伪终端,从而支持交互式会话;若 node-pty 不可用,会回退到 child_process 实现,后者不支持交互式命令。当前 schema 中该默认值即为 truerequiresRestart: true,修改后需重启生效)。

{
  "tools": {
    "shell": {
      "enableInteractiveShell": true
    }
  }
}

执行路径选择逻辑在 ShellExecutionService.execute()shouldUseNodePty 为真时优先 executeWithPty,失败或不可用时落到 childProcessFallback

显示彩色输出(showColor)

tools.shell.showColortrue 时,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 支持需要实时用户输入的命令,例如文本编辑器(vimnano)、终端 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-256colorPAGER / GIT_PAGER 默认 cat
  • git 全局与系统配置指向 /dev/nullGIT_CONFIG_GLOBALGIT_CONFIG_SYSTEMGIT_CONFIG_NOSYSTEM=1),避免读取用户级 git 配置;
  • 通过 GIT_CONFIG_KEY_*/GIT_CONFIG_VALUE_* 显式禁用 credential.helpercore.hooksPathcore.sshCommandcore.fsmonitorcore.editordiff.external 等,防止子命令通过 hooks / ssh 执行意外代码;
  • 禁用交互凭证提示:GIT_TERMINAL_PROMPT=0GIT_ASKPASSSSH_ASKPASS 置空、GH_PROMPT_DISABLED=1,以及清理 DISPLAYDBUS_SESSION_BUS_ADDRESS 等会话变量;
  • 其余环境变量经过 sanitizeEnvironment 脱敏,仅允许白名单变量(以及 GIT_CONFIG_* 系列)穿透。

命令限制(tools.core / tools.exclude)

警告tools.core所有内置工具的 allowlist,而不是仅针对 shell 命令。一旦给 tools.core 设置了任何值,只有被明确列出的工具会被启用,包括 read_filewrite_fileglobgrep_searchlist_directoryreplace 等全部内置工具。

可以通过配置文件中 tools.coretools.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 命令。

验证逻辑被设计得安全且灵活:

  1. 禁用命令链:工具会自动拆分用 &&||; 串联的命令,对每一段分别校验。链中任何一段不被允许,整个命令都会被阻止。
  2. 前缀匹配:例如允许 git 后,可以运行 git statusgit log
  3. 黑名单优先tools.exclude 列表总是先检查。命令若匹配被阻止的前缀即被拒绝,即使它同时匹配 tools.core 中的允许前缀。

命令限制示例

只允许特定命令前缀:仅允许 gitnpm,阻止其他一切命令:

{
  "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.coretools.exclude 中,将被阻止。

使用与安全提示

典型用例:

  • 运行构建脚本与测试套件;
  • 初始化或管理版本控制系统;
  • 安装项目依赖;
  • 启动开发服务器或后台监听进程。

文档中的注意事项(结合源码印证):

  • 安全:执行命令时要格外谨慎,尤其是由用户输入构造的命令,防止注入类安全漏洞——源码层的命令替换检测正是这道防线之一;
  • 错误处理:检查 StderrErrorExit Code 字段判断命令是否成功;
  • 后台进程:命令以 & 放入后台时,工具会立即返回,进程继续在后台运行,Background PIDs 字段包含其 PID。

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐