Bash 工具
让 Claude 请求 shell 命令,由您的应用程序在持久化的 bash 会话中运行,并将结果作为工具结果返回。
Bash 工具是一种 client tool(客户端工具):Claude 本身不会运行命令。当您在请求中包含该工具时,Claude 会回复一个 tool_use 块,其中指明要运行的命令。您的应用程序在其拥有的 bash 会话中运行该命令,并在 tool_result 块中返回输出。
您的应用程序在多次工具调用之间保持一个 bash 进程存活,因此状态会在命令之间持久保留。工作目录、环境变量以及命令创建的任何文件在下一条命令执行时仍然存在。
该工具的当前版本为 bash_20250124。有关模型支持、beta 头以及早期版本的信息,请参阅工具版本。有关 Anthropic 提供的所有工具,请参阅工具参考。
使用场景
- 开发工作流: 运行构建命令、测试和开发工具
- 系统自动化: 执行脚本、管理文件、自动化任务
- 数据处理: 处理文件、运行分析脚本、管理数据集
- 环境设置: 安装软件包、配置环境
快速开始
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=[{"type": "bash_20250124", "name": "bash"}],
messages=[
{"role": "user", "content": "List all Python files in the current directory."}
],
)
print(response)Claude 会以 stop_reason: "tool_use" 和一个 tool_use 块进行响应,其中包含供您的应用程序运行的命令:
{
"id": "msg_01XAbCDeFgHiJkLmNoPQrStU",
"model": "claude-opus-5-5",
"stop_reason": "tool_use",
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll list all Python files in the current directory for you."
},
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "bash",
"input": {
"command": "ls *.py"
}
}
]
}在您的 bash 会话中运行 input.command,并将输出作为 tool_result 发送回去。有关完整的往返流程,请参阅实现 bash 工具。
工作原理
每次工具调用都是 Claude 与您的应用程序之间的一次往返:
- Claude 返回一个包含要运行的
command的tool_use块。 - 您的应用程序在其 bash 会话中运行该命令。
- 您的应用程序将命令的输出(stdout 和 stderr 合并在一起)通过
tool_result块返回给 Claude。 - Claude 要么在同一会话中请求另一条命令,要么以文本进行响应。
Claude 也可能在一次响应中返回多个 tool_use 块。请在同一会话中按顺序运行它们,并在一条 user 消息中返回所有结果。请参阅并行工具使用。
API 是无状态的。您的 shell 会话的任何信息都不会在请求之间传递,因此由您的应用程序决定会话何时开始、存活多久以及何时重启。有关完整的请求和响应周期,请参阅处理工具调用。
参数
Bash 工具定义有两个必填字段:type 和 name,且 name 必须为 bash。该工具是无 schema 的:您无需提供 input_schema,因为 schema 已内置于 Claude 的模型中且无法修改。下表列出了 Claude 调用该工具时设置的输入字段。
| 参数 | 必填 | 描述 |
|---|---|---|
command | 是* | 要运行的 bash 命令 |
restart | 否 | 设置为 true 以重启 bash 会话 |
*除非使用 restart,否则为必填
要处理 restart: true,请终止 shell 进程,启动一个新进程,并返回一个确认重启的 tool_result。重启后的会话从干净状态开始:工作目录、环境变量以及任何正在运行的进程都将消失。
运行命令:
{
"command": "ls -la *.py"
}重启会话:
{
"restart": true
}工具版本
bash_20250124 是该工具的当前版本,不需要 beta 头。从 Claude Sonnet 3.7(已退役)起的每个模型都接受它,包括所有当前的 Claude 模型。
最初的 bash_20241022 版本仅适用于 2024 年 10 月的 Claude Sonnet 3.5 模型(已退役)。使用它的请求需要 anthropic-beta: computer-use-2024-10-22 头,并且 SDK 仅在其 beta 命名空间中公开它。新的集成应使用 bash_20250124。
示例:多步骤自动化
Claude 可以跨多次工具调用串联命令,以完成多步骤任务:
User request:
"Install the requests library and create a simple Python script that
fetches a joke from an API, then run it."
Claude's tool uses:
1. Install package
{"command": "pip install requests"}
2. Create script
{"command": "cat > fetch_joke.py << 'EOF'\nimport requests\nresponse = requests.get('https://official-joke-api.appspot.com/random_joke')\njoke = response.json()\nprint(f\"Setup: {joke['setup']}\")\nprint(f\"Punchline: {joke['punchline']}\")\nEOF"}
3. Run script
{"command": "python fetch_joke.py"}会话在命令之间保持状态,因此在第 2 步中创建的文件在第 3 步中可用。
实现 bash 工具
Claude 决定运行哪条命令。其他一切都由您的应用程序负责:shell 进程、超时以及安全检查。以下步骤展示了一个最小实现。
创建持久化的 bash 会话
启动一个长期存活的 bash 进程,并在其中运行每条命令。由于连接到存活进程的管道永远不会报告文件结束(end-of-file),会话会在每条命令之后打印一行唯一的哨兵(sentinel)行,以标记该命令输出的结束位置:
import subprocess import uuid class BashSession: """A bash process that stays alive between commands so state persists.""" def __init__(self): self.process = subprocess.Popen( ["/bin/bash"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # interleave errors with output, in order start_new_session=True, # own process group: a timeout can kill every child text=True, ) def execute_command(self, command): """Run a command in the session and return its output.""" sentinel = f"__CLAUDE_BASH_DONE_{uuid.uuid4().hex}__" # unique per call self.process.stdin.write(f"{command}\necho {sentinel}\n") self.process.stdin.flush() output = [] for line in self.process.stdout: if sentinel in line: # this command's output is complete break output.append(line) return "".join(output) def restart(self): self.process.kill() self.process.wait() self.__init__() bash_session = BashSession() print(bash_session.execute_command("cd /tmp && pwd")) print(bash_session.execute_command("pwd")) # still /tmp: the session kept its state会话将 stderr 与 stdout 交错输出,因此错误消息会出现在其发生的位置。该示例省略了完整实现还需要的内容:一个超时机制,当命令挂起时终止 shell 及其启动的所有进程,然后重启会话。使用命令超时最佳实践展示了一种添加方式。
处理 Claude 的工具调用
从 Claude 的响应中提取并运行命令:
tool_results = [] for content in response.content: if content.type == "tool_use" and content.name == "bash": if content.input.get("restart"): bash_session.restart() result = "Bash session restarted" else: command = content.input.get("command") result = bash_session.execute_command(command) # 每个 tool_use 块对应一个 tool_result,全部在下一条用户消息中返回 tool_results.append( {"type": "tool_result", "tool_use_id": content.id, "content": result} )将结果返回给 Claude
在延续同一对话的
user消息中将tool_result发送回去。Claude 要么在同一会话中请求另一条命令,要么完成其回答:client = anthropic.Anthropic() response = client.messages.create( model="claude-opus-5-5", max_tokens=1024, tools=[{"type": "bash_20250124", "name": "bash"}], messages=[ {"role": "user", "content": "List all Python files in the current directory."}, { "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "bash", "input": {"command": "ls *.py"}, } ], }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "analysis.py\nprocess_data.py\n", } ], }, ], ) print(response.content)当
stop_reason为tool_use时,重复"运行并返回"的循环。有关完整循环,请参阅处理客户端工具的结果。实施安全措施
添加验证和限制。使用允许列表(allowlist)而非阻止列表(blocklist):阻止列表会遗漏任何未预料到的命令。该示例还会拒绝作为独立单词出现的 shell 运算符:
import shlex ALLOWED_COMMANDS = {"ls", "cat", "echo", "pwd", "grep", "find", "wc", "head", "tail"} SHELL_OPERATORS = {"&&", "||", "|", ";", "&", ">", "<", ">>"} def validate_command(command): # 仅允许显式允许列表中的命令 try: tokens = shlex.split(command) except ValueError: return False, "Could not parse command" if not tokens: return False, "Empty command" executable = tokens[0] if executable not in ALLOWED_COMMANDS: return False, f"Command '{executable}' is not in the allowlist" # 拒绝以独立单词形式书写的 shell 运算符 for token in tokens[1:]: if token in SHELL_OPERATORS or token.startswith(("$", "`")): return False, f"Shell operator '{token}' is not allowed" return True, None此检查是针对明显错误的绊线,而非强制执行边界。它会拒绝本页其他示例中使用的带空格的链式操作(
&&)、管道和重定向。它无法捕获与单词粘连在一起的操作符,例如cat data.txt|grep x,因为分词器会将data.txt|grep保留在一个令牌内。请决定您的应用程序允许哪些命令和操作符。真正的控制手段是隔离:在容器或虚拟机中运行整个会话(请参阅安全)。
处理错误
当命令失败或会话中断时,请告知 Claude 发生了什么。将消息作为 tool_result 内容返回,并将 is_error 设置为 true,这会将该工具调用标记为失败。请参阅使用 is_error 处理错误。
如果命令执行时间过长:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Error: command did not finish within 30 seconds",
"is_error": true
}
]
}如果命令不存在:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "bash: nonexistentcommand: command not found",
"is_error": true
}
]
}如果存在权限问题:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "bash: /root/sensitive-file: Permission denied",
"is_error": true
}
]
}遵循实现最佳实践
一条永远不会结束的命令(例如等待输入的命令)会永久阻塞会话,因为它的哨兵行永远不会到达。为每条命令设置截止时间。当截止时间过去后,停止 shell 以及该命令启动的所有进程,然后重启会话:
import concurrent.futures
import os
import signal
def execute_with_timeout(session, command, timeout=30):
"""Run a command in the session, replacing the session if the command hangs."""
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
future = pool.submit(session.execute_command, command)
try:
return future.result(timeout=timeout)
except concurrent.futures.TimeoutError:
# 该进程组包含 shell 以及该命令启动的所有进程
os.killpg(session.process.pid, signal.SIGKILL)
session.restart()
return f"Error: command did not finish within {timeout} seconds"终止操作会停止挂起的命令及其启动的所有进程。将消息作为错误 tool_result 返回(请参阅处理错误),这会将该工具调用标记为失败。
保持 bash 会话持久化,以维护环境变量和工作目录:
# 在同一会话中运行的命令会保持状态
commands = [
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt", # The session is still in /tmp
]截断大型输出以防止令牌限制问题:
def truncate_output(output, max_lines=100):
lines = output.split("\n")
if len(lines) > max_lines:
truncated = "\n".join(lines[:max_lines])
return f"{truncated}\n\n... Output truncated ({len(lines)} total lines) ..."
return output保留审计记录。让每条命令都经过一个包装器,该包装器在命令运行前记录命令,在命令完成后记录输出。即使命令挂起或破坏了会话,仍会留下记录:
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
def execute_and_log(session, command):
"""Run a command in the session and keep an audit record of it."""
logging.info("command=%r", command)
output = session.execute_command(command)
logging.info("output=%r", output[:200]) # first 200 characters
return output记录默认输出到 stderr;请将其指向文件或您的日志管道以便保留。请包含能将记录与您应用程序中的请求关联起来的任何信息,例如最终用户和 tool_use_id。
安全
除隔离之外,还应添加以下控制措施:
- 在运行命令之前进行验证,使用允许列表而非阻止列表。请参阅实现 bash 工具。
- 对 shell 进程设置资源限制(CPU、内存和磁盘),例如使用
ulimit。 - 记录每条命令及其输出,以便您可以审计运行过的内容。
- 在将输出返回给 Claude 之前,对其中的凭据和其他机密信息进行脱敏处理。
定价
bash 工具定义会向您的请求中添加以下 input tokens(输入令牌)。这是在每个模型的工具使用系统提示之外额外添加的,只要存在任何工具,该系统提示就会生效。
| 模型 | 额外输入令牌 |
|---|---|
| Claude Opus 5、Claude Opus 4.8 和 Claude Opus 4.7 | 325 个令牌 |
| Claude Opus 4.6、Claude Sonnet 4.6 及更早版本 | 244 个令牌 |
以下内容会消耗额外的令牌:
- 命令输出(stdout/stderr)
- 错误消息
- 大型文件内容
有关完整的定价详情,请参阅工具使用定价。
常见模式
开发工作流
- 运行测试:
pytest && coverage report - 构建项目:
npm install && npm run build - Git 操作:
git status && git add . && git commit -m "message"
有关在长时间运行的智能体工作流中将 git 用作检查点与恢复机制的指导,请参阅状态管理最佳实践。
文件操作
- 处理数据:
wc -l *.csv && ls -lh *.csv - 搜索文件:
find . -name "*.py" | xargs grep "pattern" - 创建备份:
tar -czf backup.tar.gz ./data
系统任务
- 检查资源:
df -h && free -m - 进程管理:
ps aux | grep python - 环境设置:
export PATH=$PATH:/new/path && echo $PATH
限制
- 不支持交互式命令: 会话无法运行
vim、less、密码提示或任何在 stdin 上等待输入的命令。 - 不支持 GUI 应用程序: 会话仅限命令行。
- 会话范围: Bash 会话状态位于客户端。您的应用程序负责在轮次之间维护 shell 会话。
- 输出限制: API 不会截断工具结果(过大的请求会被拒绝)。请在将大型输出返回给 Claude 之前,在您的应用程序中对其进行截断。
- 不支持流式传输: 只有当您的应用程序在下一个请求中返回
tool_result时,输出才会到达 Claude。
与其他工具结合使用
Bash 工具与文本编辑器工具搭配使用效果很好:Claude 使用一个工具编辑文件,并使用另一个工具请求运行该文件的命令。
后续步骤
查看和修改文本文件,以调试、修复和改进代码。
将 Claude 连接到外部工具和 API。了解工具在何处执行、Claude 何时调用它们,以及哪种工具适合您的任务。
Was this page helpful?