Bash tool


Note

此功能符合零数据留存 (ZDR) 条件。当你的组织有 ZDR 安排时,通过此功能发送的数据在 API 响应返回后不会被存储。

Bash tool 使 Claude 能够在持久的 bash 会话中执行 shell 命令,实现系统操作、脚本执行和命令行自动化。Shell 访问是 agent 的基础能力。在 Terminal-Bench 2.0(一个使用纯 shell 验证评估真实终端任务的基准测试)上,Claude 在获得持久 bash 会话访问后表现出显著的性能提升。

概述

Bash tool 为 Claude 提供:

  • 维持状态的持久 bash 会话
  • 运行任何 shell 命令的能力
  • 访问环境变量和工作目录
  • 命令链和脚本能力

模型支持参见工具参考

使用场景

  • 开发工作流: 运行构建命令、测试和开发工具
  • 系统自动化: 执行脚本、管理文件、自动化任务
  • 数据处理: 处理文件、运行分析脚本、管理数据集
  • 环境设置: 安装包、配置环境

快速开始

curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-opus-4-7",
    "max_tokens": 1024,
    "tools": [
      {
        "type": "bash_20250124",
        "name": "bash"
      }
    ],
    "messages": [
      {
        "role": "user",
        "content": "List all Python files in the current directory."
      }
    ]
  }'
ant messages create \
  --model claude-opus-4-7 \
  --max-tokens 1024 \
  --tool '{type: bash_20250124, name: bash}' \
  --message '{role: user, content: List all Python files in the current directory.}'
import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    tools=[{"type": "bash_20250124", "name": "bash"}],
    messages=[
        {"role": "user", "content": "List all Python files in the current directory."}
    ],
)

print(response)
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-opus-4-7",
  max_tokens: 1024,
  tools: [{ type: "bash_20250124", name: "bash" }],
  messages: [
    {
      role: "user",
      content: "List all Python files in the current directory."
    }
  ]
});

console.log(response);
using Anthropic;
using Anthropic.Models.Messages;

var client = new AnthropicClient();

var response = await client.Messages.Create(
    new()
    {
        Model = Model.ClaudeOpus4_7,
        MaxTokens = 1024,
        Tools = [new ToolBash20250124()],
        Messages =
        [
            new()
            {
                Role = Role.User,
                Content = "List all Python files in the current directory.",
            },
        ],
    }
);

Console.WriteLine(response);
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/anthropics/anthropic-sdk-go"
)

func main() {
	client := anthropic.NewClient()

	response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeOpus4_7,
		MaxTokens: 1024,
		Tools: []anthropic.ToolUnionParam{
			{OfBashTool20250124: &anthropic.ToolBash20250124Param{}},
		},
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock("List all Python files in the current directory.")),
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.ToolBash20250124;

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    Message response = client.messages().create(
        MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_4_7)
            .maxTokens(1024)
            .addTool(ToolBash20250124.builder().build())
            .addUserMessage("List all Python files in the current directory.")
            .build()
    );

    IO.println(response);
}
<?php

use Anthropic\Client;
use Anthropic\Messages\ToolBash20250124;

$client = new Client();

$response = $client->messages->create(
    model: 'claude-opus-4-7',
    maxTokens: 1024,
    tools: [new ToolBash20250124()],
    messages: [
        ['role' => 'user', 'content' => 'List all Python files in the current directory.'],
    ],
);

echo $response;
require "anthropic"

client = Anthropic::Client.new

response = client.messages.create(
  model: "claude-opus-4-7",
  max_tokens: 1024,
  tools: [{type: "bash_20250124", name: "bash"}],
  messages: [
    {role: "user", content: "List all Python files in the current directory."}
  ]
)

puts response

工作原理

Bash tool 维护一个持久会话:

  1. Claude 确定要运行的命令
  2. 你在 bash shell 中执行命令
  3. 将输出(stdout 和 stderr)返回给 Claude
  4. 会话状态在命令之间持久化(环境变量、工作目录)

参数

参数必填描述
command是*要运行的 bash 命令
restart设置为 true 以重启 bash 会话

*除非使用 restart,否则必填

使用示例

运行命令:

{
  "command": "ls -la *.py"
}

重启会话:

{
  "restart": true
}

示例:多步骤自动化

Claude 可以链接命令来完成复杂任务:

用户请求:
"Install the requests library and create a simple Python script that
fetches a joke from an API, then run it."

Claude 的工具调用:
1. 安装包
   {"command": "pip install requests"}

2. 创建脚本
   {"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. 运行脚本
   {"command": "python fetch_joke.py"}

会话在命令之间维护状态,因此步骤 2 中创建的文件在步骤 3 中可用。

实现 bash tool

Bash tool 作为无 schema 工具实现。使用此工具时,你不需要像其他工具那样提供输入 schema;schema 内置在 Claude 的模型中,无法修改。

  1. 设置 bash 环境

    创建一个 Claude 可以交互的持久 bash 会话:

    import subprocess
    import threading
    import queue
    
    
    class BashSession:
        def __init__(self):
            self.process = subprocess.Popen(
                ["/bin/bash"],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                bufsize=0,
            )
            self.output_queue = queue.Queue()
            self.error_queue = queue.Queue()
            self._start_readers()
    
        def _start_readers(self): ...
    
  2. 处理命令执行

    创建一个执行命令并捕获输出的函数:

    class BashSession:
        def _read_output(self, timeout): ...
        def execute_command(self, command):
            # 将命令发送到 bash
            self.process.stdin.write(command + "\n")
            self.process.stdin.flush()
    
            # 捕获带超时的输出
            output = self._read_output(timeout=10)
            return output
    
        process = None
    
  3. 处理 Claude 的工具调用

    从 Claude 的响应中提取并执行命令:

    from types import SimpleNamespace as _SN
    
    response = _SN(
        content=[_SN(type="tool_use", name="bash", input={"command": "ls"}, id="toolu_01")]
    )
    bash_session = _SN(restart=lambda: None, execute_command=lambda c: "output")
    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)
    
            # 将结果返回给 Claude
            tool_result = {
                "type": "tool_result",
                "tool_use_id": content.id,
                "content": result,
            }
    
  4. 实现安全措施

    添加验证和限制。使用允许列表而非阻止列表,因为阻止列表很容易被绕过。拒绝 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(("{{CONTENT}}quot;, "`")):
                return False, f"Shell operator '{token}' is not allowed"
    
        return True, None
    

    此检查是第一道防线。要获得更强的隔离,请使用 shell=False 运行已验证的命令,并将 shlex.split(command) 作为参数列表传递,这样 shell 就不会解释字符串。

错误处理

实现 bash tool 时,处理各种错误场景:

命令执行超时

如果命令执行时间过长:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
      "content": "Error: Command timed out after 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
    }
  ]
}

遵循实现最佳实践

使用命令超时

实现超时以防止命令挂起:

import subprocess


def execute_with_timeout(command, timeout=30):
    try:
        result = subprocess.run(
            command, shell=True, capture_output=True, text=True, timeout=timeout
        )
        return result.stdout + result.stderr
    except subprocess.TimeoutExpired:
        return f"Command timed out after \{timeout\} seconds"

维护会话状态

保持 bash 会话持久化以维护环境变量和工作目录:

# 在同一会话中运行的命令会维护状态
commands = [
    "cd /tmp",
    "echo 'Hello' > test.txt",
    "cat test.txt",  # 这可以工作,因为我们仍在 /tmp 中
]

处理大型输出

截断非常大的输出以防止 token 限制问题:

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


def log_command(command, output, user_id):
    logging.info(f"User \{user_id\} executed: \{command\}")
    logging.info(f"Output: {output[:200]}...")  # 记录前 200 个字符

清理输出

从命令输出中移除敏感信息:

def sanitize_output(output):
    # 移除潜在的密钥或凭证
    import re

    # 示例:移除 AWS 凭证
    output = re.sub(r"aws_access_key_id\s*=\s*\S+", "aws_access_key_id=***", output)
    output = re.sub(
        r"aws_secret_access_key\s*=\s*\S+", "aws_secret_access_key=***", output
    )
    return output

安全

Warning

Bash tool 提供直接的系统访问。请实施以下基本安全措施:

  • 在隔离环境中运行(Docker/VM)
  • 实施命令过滤和允许列表
  • 设置资源限制(CPU、内存、磁盘)
  • 记录所有已执行的命令

关键建议

  • 使用 ulimit 设置资源约束
  • 过滤危险命令(sudorm -rf 等)
  • 以最小用户权限运行
  • 监控和记录所有命令执行

定价

Bash tool 为你的 API 调用增加 245 个输入 token

以下内容会消耗额外 token:

  • 命令输出(stdout/stderr)
  • 错误消息
  • 大型文件内容

完整的定价详情参见工具使用定价

常见模式

开发工作流

  • 运行测试:pytest && coverage report
  • 构建项目:npm install && npm run build
  • Git 操作:git status && git add . && git commit -m "message"

基于 Git 的检查点

Git 在长时间运行的 agent 工作流中作为结构化的恢复机制,而不仅仅是保存更改的方式:

  • 捕获基线: 在任何 agent 工作开始之前,提交当前状态。这是已知良好的起点。
  • 按功能提交: 每个完成的功能都有自己的提交。如果后续出现问题,这些提交可以作为回滚点。
  • 在会话开始时重建状态: 读取 git log 和进度文件以了解已完成的工作和接下来的内容。
  • 失败时回滚: 如果工作出错,git checkout 会回退到最后一个良好的提交,而不是尝试调试损坏的状态。

文件操作

  • 处理数据: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

限制

  • 无交互式命令: 无法处理 vimless 或密码提示
  • 无 GUI 应用: 仅限命令行
  • 会话范围: Bash 会话状态在客户端。API 是无状态的。你的应用程序负责在轮次之间维护 shell 会话。
  • 输出限制: 大型输出可能被截断
  • 无流式传输: 结果在完成后返回

与其他工具组合

Bash tool 与文本编辑器和其他工具组合使用时最强大。

Note

如果你同时使用 code execution tool,Claude 可以访问两个独立的执行环境:你的本地 bash 会话和 Anthropic 的沙箱容器。状态不会在它们之间共享。有关提示 Claude 区分环境的指导,请参见将代码执行与其他执行工具一起使用

下一步