Bash 工具是一种 client tool(客户端工具):Claude 本身不会运行命令。当您在请求中包含该工具时,Claude 会回复一个 tool_use 块,其中指明要运行的命令。您的应用程序在其拥有的 bash 会话中运行该命令,并在 tool_result 块中返回输出。
您的应用程序在多次工具调用之间保持一个 bash 进程存活,因此状态会在命令之间持久保留。工作目录、环境变量以及命令创建的任何文件在下一条命令执行时仍然存在。
该工具的当前版本为 bash_20250124。有关模型支持、beta 头以及早期版本的信息,请参阅工具版本。有关 Anthropic 提供的所有工具,请参阅工具参考。
使用场景
- 开发工作流: 运行构建命令、测试和开发工具
- 系统自动化: 运行脚本、管理文件、自动化任务
- 数据处理: 处理文件、运行分析脚本、管理数据集
- 环境设置: 安装软件包、配置环境
快速开始
cURL
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-5-5",
"max_tokens": 1024,
"tools": [
{
"type": "bash_20250124",
"name": "bash"
}
],
"messages": [
{
"role": "user",
"content": "List all Python files in the current directory."
}
]
}'
CLI
ant messages create \
--model claude-opus-5-5 \
--max-tokens 1024 \
--tool '{type: bash_20250124, name: bash}' \
--message '{role: user, content: List all Python files in the current directory.}'
Python
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)
TypeScript
const client = new Anthropic();
const response = await 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."
}
]
});
console.log(response);
C#
var client = new AnthropicClient();
var response = await client.Messages.Create(
new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = [new ToolBash20250124()],
Messages =
[
new UserMessageParam { Content = "List all Python files in the current directory." },
],
}
);
Console.WriteLine(response);
Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
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)
Java
import com.anthropic.models.messages.ToolBash20250124;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
Message response = client.messages().create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addTool(ToolBash20250124.builder().build())
.addUserMessage("List all Python files in the current directory.")
.build()
);
IO.println(response);
}
PHP
use Anthropic\Messages\Model;
use Anthropic\Messages\ToolBash20250124;
$client = new Client();
$response = $client->messages->create(
model: Model::CLAUDE_OPUS_5_5,
maxTokens: 1024,
tools: [new ToolBash20250124()],
messages: [
['role' => 'user', 'content' => 'List all Python files in the current directory.'],
],
);
echo $response;
Ruby
client = Anthropic::Client.new
response = client.messages.create(
model: Anthropic::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."}
]
)
puts response
Claude 会以 stop_reason: "tool_use" 和一个 tool_use 块进行响应,其中包含供您的应用程序运行的命令:
Output
{
"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)行,以标记该命令输出的结束位置:
Python
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 for each 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 stateTypeScript
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; import { createInterface, type Interface } from "node:readline"; import { randomUUID } from "node:crypto"; // 一个在命令之间保持存活的 bash 进程,以便状态得以持久化。 class BashSession { process!: ChildProcessWithoutNullStreams; private lines!: Interface; constructor() { this.start(); } private start(): void { this.process = spawn("/bin/bash", { detached: true // own process group: a timeout can kill every child }); this.process.stdin.write("exec 2>&1\n"); // interleave errors with output, in order this.lines = createInterface({ input: this.process.stdout }); } // 在会话中运行命令并返回其输出。 executeCommand(command: string): Promise<string> { const sentinel = `__CLAUDE_BASH_DONE_${randomUUID()}__`; // unique for each call const output: string[] = []; const result = new Promise<string>((resolve) => { const onLine = (line: string): void => { if (line.includes(sentinel)) { // 此命令的输出已完成 this.lines.off("line", onLine); resolve(output.join("")); } else { output.push(`${line}\n`); } }; this.lines.on("line", onLine); }); this.process.stdin.write(`${command}\necho ${sentinel}\n`); return result; } restart(): void { this.process.kill("SIGKILL"); this.lines.close(); this.start(); } } const session = new BashSession(); console.log(await session.executeCommand("cd /tmp && pwd")); console.log(await session.executeCommand("pwd")); // still /tmp: the session kept its state session.process.stdin.end(); // closing stdin ends the shell so the script can exitC#
using System.Diagnostics; using System.Text; var session = new BashSession(); Console.Write(session.ExecuteCommand("cd /tmp && pwd")); Console.Write(session.ExecuteCommand("pwd")); // still /tmp: the session kept its state // 一个在命令之间保持存活的 bash 进程,以便状态得以持久化。 class BashSession { public Process Process { get; private set; } public BashSession() { Process = Start(); } static Process Start() { var process = Process.Start(new ProcessStartInfo("/bin/bash") { RedirectStandardInput = true, RedirectStandardOutput = true })!; process.StandardInput.Write("exec 2>&1\n"); // interleave errors with output, in order process.StandardInput.Flush(); return process; } // 在会话中运行命令并返回其输出。 public string ExecuteCommand(string command) { var sentinel = $"__CLAUDE_BASH_DONE_{Guid.NewGuid():N}__"; // unique for each call Process.StandardInput.Write($"{command}\necho {sentinel}\n"); Process.StandardInput.Flush(); var output = new StringBuilder(); while (Process.StandardOutput.ReadLine() is string line) { if (line.Contains(sentinel)) // this command's output is complete { break; } output.Append(line).Append('\n'); } return output.ToString(); } public void Restart() { Process.Kill(entireProcessTree: true); Process.WaitForExit(); Process = Start(); } }Go
import ( "bufio" "crypto/rand" "encoding/hex" "fmt" "io" "log" "os/exec" "strings" "syscall" ) // BashSession 是一个在命令之间保持存活的 bash 进程,因此状态得以持久保留。 type BashSession struct { cmd *exec.Cmd stdin io.WriteCloser output *bufio.Reader } func NewBashSession() (*BashSession, error) { cmd := exec.Command("/bin/bash") cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} // own process group: a timeout can kill every child stdin, err := cmd.StdinPipe() if err != nil { return nil, err } stdout, err := cmd.StdoutPipe() if err != nil { return nil, err } cmd.Stderr = cmd.Stdout // interleave errors with output, in order if err := cmd.Start(); err != nil { return nil, err } return &BashSession{cmd: cmd, stdin: stdin, output: bufio.NewReader(stdout)}, nil } // ExecuteCommand 在会话中运行命令并返回其输出。 func (s *BashSession) ExecuteCommand(command string) string { buf := make([]byte, 16) rand.Read(buf) sentinel := fmt.Sprintf("__CLAUDE_BASH_DONE_%s__", hex.EncodeToString(buf)) // unique for each call fmt.Fprintf(s.stdin, "%s\necho %s\n", command, sentinel) var output strings.Builder for { line, err := s.output.ReadString('\n') if err != nil || strings.Contains(line, sentinel) { // this command's output is complete break } output.WriteString(line) } return output.String() } // Restart 终止当前 shell,并在原位置启动一个全新的会话。 func (s *BashSession) Restart() error { s.cmd.Process.Kill() s.cmd.Wait() fresh, err := NewBashSession() if err != nil { return err } *s = *fresh return nil } func main() { session, err := NewBashSession() if err != nil { log.Fatal(err) } fmt.Print(session.ExecuteCommand("cd /tmp && pwd")) fmt.Print(session.ExecuteCommand("pwd")) // still /tmp: the session kept its state }Java
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.UUID; // 一个在命令之间保持存活的 bash 进程,以便状态得以持久化。 class BashSession { Process process; BufferedWriter stdin; BufferedReader output; BashSession() throws IOException { start(); } void start() throws IOException { ProcessBuilder builder = new ProcessBuilder("/bin/bash"); builder.redirectErrorStream(true); // interleave errors with output, in order process = builder.start(); stdin = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); output = new BufferedReader(new InputStreamReader(process.getInputStream())); } // 在会话中运行命令并返回其输出。 String executeCommand(String command) throws IOException { String sentinel = "__CLAUDE_BASH_DONE_" + UUID.randomUUID() + "__"; // unique for each call stdin.write(command + "\necho " + sentinel + "\n"); stdin.flush(); StringBuilder result = new StringBuilder(); String line; while ((line = output.readLine()) != null) { if (line.contains(sentinel)) { // this command's output is complete break; } result.append(line).append("\n"); } return result.toString(); } void restart() throws IOException, InterruptedException { process.destroyForcibly(); process.waitFor(); start(); } } void main() throws Exception { BashSession session = new BashSession(); IO.println(session.executeCommand("cd /tmp && pwd")); IO.println(session.executeCommand("pwd")); // still /tmp: the session kept its state }PHP
// 一个在命令之间保持存活的 bash 进程,以便状态得以持久化。 class BashSession { public $process; public $stdin; public $output; public function __construct() { $this->start(); } private function start(): void { // setsid 为 shell 提供独立的进程组:超时时可终止所有子进程 $this->process = proc_open( ['setsid', '/bin/bash'], [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['redirect', 1]], // interleave errors with output $pipes ); $this->stdin = $pipes[0]; $this->output = $pipes[1]; } // 在会话中运行命令并返回其输出。 public function executeCommand(string $command): string { $sentinel = '__CLAUDE_BASH_DONE_' . bin2hex(random_bytes(16)) . '__'; // unique for each call fwrite($this->stdin, "{$command}\necho {$sentinel}\n"); fflush($this->stdin); $output = ''; while (($line = fgets($this->output)) !== false) { if (str_contains($line, $sentinel)) { // this command's output is complete break; } $output .= $line; } return $output; } public function restart(): void { proc_terminate($this->process, 9); proc_close($this->process); $this->start(); } } $session = new BashSession(); echo $session->executeCommand('cd /tmp && pwd'); echo $session->executeCommand('pwd'); // still /tmp: the session kept its stateRuby
require "open3" require "securerandom" # 一个在命令之间保持存活的 bash 进程,以便状态得以持久化。 class BashSession attr_reader :output, :wait_thread def initialize start end # 在会话中运行命令并返回其输出。 def execute_command(command) sentinel = "__CLAUDE_BASH_DONE_#{SecureRandom.hex(16)}__" # unique for each call @stdin.write("#{command}\necho #{sentinel}\n") @stdin.flush output = +"" @output.each_line do |line| break if line.include?(sentinel) # this command's output is complete output << line end output end def restart Process.kill("KILL", @wait_thread.pid) @wait_thread.join start end private def start # popen2e 将错误与输出按顺序交错;pgroup 为 shell 分配 # 独立的进程组,以便超时时可以终止所有子进程 @stdin, @output, @wait_thread = Open3.popen2e("/bin/bash", pgroup: true) end end session = BashSession.new puts session.execute_command("cd /tmp && pwd") puts session.execute_command("pwd") # still /tmp: the session kept its state该会话将 stderr 与 stdout 交错输出,因此错误消息会出现在其发生的位置。该示例省略了完整实现还需要的内容:当命令挂起时,通过超时终止 shell 及其启动的所有进程,然后重启会话。使用命令超时最佳实践展示了一种添加方式。
-
处理 Claude 的工具调用
从 Claude 的响应中提取并运行命令:
Python
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} )TypeScript
const toolResults: { type: string; tool_use_id: string; content: string }[] = []; for (const block of response.content) { if (block.type === "tool_use" && block.name === "bash") { let result: string; if (block.input.restart) { bashSession.restart(); result = "Bash session restarted"; } else { result = await bashSession.executeCommand(block.input.command ?? ""); } // 每个 tool_use 块对应一个 tool_result,全部在下一条用户消息中返回 toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result }); } }C#
var toolResults = new List<ToolResultBlockParam>(); foreach (var block in response.Content) { if (block.TryPickToolUse(out var toolUse) && toolUse.Name == "bash") { string result; if (toolUse.Input.TryGetValue("restart", out var restart) && restart.GetBoolean()) { bashSession.Restart(); result = "Bash session restarted"; } else { var command = toolUse.Input["command"].GetString() ?? ""; result = bashSession.ExecuteCommand(command); } // 每个 tool_use 块对应一个 tool_result,全部在下一条用户消息中返回 toolResults.Add(new ToolResultBlockParam { ToolUseID = toolUse.ID, Content = result }); } }Go
var toolResults []anthropic.ContentBlockParamUnion for _, block := range response.Content { if block.Type == "tool_use" && block.Name == "bash" { var input struct { Command string `json:"command"` Restart bool `json:"restart"` } if err := json.Unmarshal(block.Input, &input); err != nil { log.Fatal(err) } var result string if input.Restart { bashSession.Restart() result = "Bash session restarted" } else { result = bashSession.ExecuteCommand(input.Command) } // 每个 tool_use 块对应一个 tool_result,全部在下一条 user 消息中返回 toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, result, false)) } }Java
List<Map<String, Object>> toolResults = new ArrayList<>(); for (ContentBlock block : response.content()) { if (block.type().equals("tool_use") && block.name().equals("bash")) { String result; if (Boolean.TRUE.equals(block.input().get("restart"))) { bashSession.restart(); result = "Bash session restarted"; } else { String command = (String) block.input().get("command"); result = bashSession.executeCommand(command); } // 每个 tool_use 块对应一个 tool_result,全部在下一条用户消息中返回 toolResults.add(Map.of("type", "tool_result", "tool_use_id", block.id(), "content", result)); } }PHP
$toolResults = []; foreach ($response->content as $block) { if ($block->type === 'tool_use' && $block->name === 'bash') { if (!empty($block->input['restart'])) { $bashSession->restart(); $result = 'Bash session restarted'; } else { $result = $bashSession->executeCommand($block->input['command']); } // 每个 tool_use 块对应一个 tool_result,全部在下一条 user 消息中返回 $toolResults[] = ['type' => 'tool_result', 'tool_use_id' => $block->id, 'content' => $result]; } }Ruby
tool_results = [] response.content.each do |block| next unless block.type == :tool_use && block.name == "bash" result = if block.input[:restart] bash_session.restart "Bash session restarted" else bash_session.execute_command(block.input[:command]) end # 每个 tool_use 块对应一个 tool_result,全部在下一条用户消息中返回 tool_results << {type: "tool_result", tool_use_id: block.id, content: result} end -
将结果返回给 Claude
在延续同一对话的
user消息中发回tool_result。Claude 要么在同一会话中请求另一条命令,要么完成其回答:cURL
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-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" } ] } ] }'CLI
ant messages create <<'YAML' 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 process_data.py YAMLPython
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)TypeScript
const client = new Anthropic(); const response = await 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" } ] } ] }); console.log(response.content);C#
var client = new AnthropicClient(); var response = await client.Messages.Create( new() { Model = Model.ClaudeOpus5_5, MaxTokens = 1024, Tools = [new ToolBash20250124()], Messages = [ new UserMessageParam { Content = "List all Python files in the current directory." }, new AssistantMessageParam { Content = new List<ContentBlockParam> { new ContentBlockParam(new ToolUseBlockParam() { ID = "toolu_01A09q90qw90lq917835lq9", Name = "bash", Input = new Dictionary<string, JsonElement> { ["command"] = JsonSerializer.SerializeToElement("ls *.py"), }, }), }, }, new UserMessageParam { Content = new List<ContentBlockParam> { new ContentBlockParam(new ToolResultBlockParam() { ToolUseID = "toolu_01A09q90qw90lq917835lq9", Content = "analysis.py\nprocess_data.py\n", }), }, }, ], } ); Console.WriteLine(response);Go
client := anthropic.NewClient() response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ Model: anthropic.ModelClaudeOpus5_5, MaxTokens: 1024, Tools: []anthropic.ToolUnionParam{ {OfBashTool20250124: &anthropic.ToolBash20250124Param{}}, }, Messages: []anthropic.MessageParam{ anthropic.NewUserMessage(anthropic.NewTextBlock("List all Python files in the current directory.")), anthropic.NewAssistantMessage( anthropic.NewToolUseBlock( "toolu_01A09q90qw90lq917835lq9", map[string]any{"command": "ls *.py"}, "bash", ), ), anthropic.NewUserMessage( anthropic.NewToolResultBlock( "toolu_01A09q90qw90lq917835lq9", "analysis.py\nprocess_data.py\n", false, ), ), }, }) if err != nil { log.Fatal(err) } fmt.Println(response.Content)Java
import com.anthropic.core.JsonValue; import com.anthropic.models.messages.ContentBlockParam; // ... import com.anthropic.models.messages.ToolBash20250124; import com.anthropic.models.messages.ToolResultBlockParam; import com.anthropic.models.messages.ToolUseBlockParam; // ... void main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); MessageCreateParams params = MessageCreateParams.builder() .model(Model.CLAUDE_OPUS_5_5) .maxTokens(1024) .addTool(ToolBash20250124.builder().build()) .addUserMessage("List all Python files in the current directory.") .addAssistantMessageOfContentBlockParams( List.of( ContentBlockParam.ofToolUse( ToolUseBlockParam.builder() .id("toolu_01A09q90qw90lq917835lq9") .name("bash") .input( ToolUseBlockParam.Input.builder() .putAdditionalProperty("command", JsonValue.from("ls *.py")) .build() ) .build() ) ) ) .addUserMessageOfContentBlockParams( List.of( ContentBlockParam.ofToolResult( ToolResultBlockParam.builder() .toolUseId("toolu_01A09q90qw90lq917835lq9") .content("analysis.py\nprocess_data.py\n") .build() ) ) ) .build(); Message response = client.messages().create(params); IO.println(response.content()); }PHP
use Anthropic\Messages\Model; use Anthropic\Messages\ToolBash20250124; $client = new Client(); $response = $client->messages->create( model: Model::CLAUDE_OPUS_5_5, maxTokens: 1024, tools: [new ToolBash20250124()], 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_r($response->content);Ruby
client = Anthropic::Client.new response = client.messages.create( model: Anthropic::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" } ] } ] ) puts response.content当
stop_reason为tool_use时,重复“运行并返回”的循环。有关完整循环,请参阅处理客户端工具的结果。 -
实施安全措施
添加验证和限制。使用允许列表(allowlist)而非阻止列表(blocklist):阻止列表会遗漏任何未预料到的命令。该示例还会拒绝作为独立单词出现的 shell 运算符:
Python
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, NoneTypeScript
const ALLOWED_COMMANDS = new Set([ "ls", "cat", "echo", "pwd", "grep", "find", "wc", "head", "tail" ]); const SHELL_OPERATORS = new Set(["&&", "||", "|", ";", "&", ">", "<", ">>"]); function validateCommand(command: string): { ok: boolean; reason?: string } { // 按空白字符拆分:足以用于绊线式检查 const tokens = command.split(/\s+/).filter((token) => token.length > 0); if (tokens.length === 0) { return { ok: false, reason: "Empty command" }; } // 仅允许显式允许列表中的命令 const executable = tokens[0]; if (!ALLOWED_COMMANDS.has(executable)) { return { ok: false, reason: `Command '${executable}' is not in the allowlist` }; } // 拒绝以独立单词形式书写的 shell 运算符 for (const token of tokens.slice(1)) { const bare = token.replace(/^["']+/, ""); // a quoted token can still smuggle an expansion if (SHELL_OPERATORS.has(token) || bare.startsWith("$") || bare.startsWith("`")) { return { ok: false, reason: `Shell operator '${token}' is not allowed` }; } } return { ok: true }; }C#
var allowedCommands = new HashSet<string> { "ls", "cat", "echo", "pwd", "grep", "find", "wc", "head", "tail" }; var shellOperators = new HashSet<string> { "&&", "||", "|", ";", "&", ">", "<", ">>" }; (bool Ok, string? Reason) ValidateCommand(string command) { // 按空白字符拆分:足以用于绊线式检查 var tokens = command.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length == 0) { return (false, "Empty command"); } // 仅允许显式允许列表中的命令 var executable = tokens[0]; if (!allowedCommands.Contains(executable)) { return (false, $"Command '{executable}' is not in the allowlist"); } // 拒绝以独立单词形式书写的 shell 运算符 foreach (var token in tokens.Skip(1)) { var bare = token.TrimStart('"', '\''); // a quoted token can still smuggle an expansion if (shellOperators.Contains(token) || bare.StartsWith('$') || bare.StartsWith('`')) { return (false, $"Shell operator '{token}' is not allowed"); } } return (true, null); }Go
var allowedCommands = map[string]bool{ "ls": true, "cat": true, "echo": true, "pwd": true, "grep": true, "find": true, "wc": true, "head": true, "tail": true, } var shellOperators = map[string]bool{ "&&": true, "||": true, "|": true, ";": true, "&": true, ">": true, "<": true, ">>": true, } func validateCommand(command string) (bool, string) { // 按空白字符拆分:足以用于绊线式检查 tokens := strings.Fields(command) if len(tokens) == 0 { return false, "Empty command" } // 仅允许显式允许列表中的命令 executable := tokens[0] if !allowedCommands[executable] { return false, fmt.Sprintf("Command %q is not in the allowlist", executable) } // 拒绝以独立单词形式书写的 shell 操作符 for _, token := range tokens[1:] { bare := strings.TrimLeft(token, `"'`) // a quoted token can still smuggle an expansion if shellOperators[token] || strings.HasPrefix(bare, "$") || strings.HasPrefix(bare, "`") { return false, fmt.Sprintf("Shell operator %q is not allowed", token) } } return true, "" }Java
import java.util.List; import java.util.Set; static final Set<String> ALLOWED_COMMANDS = Set.of("ls", "cat", "echo", "pwd", "grep", "find", "wc", "head", "tail"); static final Set<String> SHELL_OPERATORS = Set.of("&&", "||", "|", ";", "&", ">", "<", ">>"); record Validation(boolean ok, String reason) {} Validation validateCommand(String command) { // 按空白字符拆分:足以用于绊线式检查 List<String> tokens = List.of(command.trim().split("\\s+")); if (tokens.size() == 1 && tokens.get(0).isEmpty()) { return new Validation(false, "Empty command"); } // 仅允许显式允许列表中的命令 String executable = tokens.get(0); if (!ALLOWED_COMMANDS.contains(executable)) { return new Validation(false, "Command '" + executable + "' is not in the allowlist"); } // 拒绝以独立单词形式书写的 shell 运算符 for (String token : tokens.subList(1, tokens.size())) { String bare = token.replaceFirst("^[\"']+", ""); // a quoted token can still smuggle an expansion if (SHELL_OPERATORS.contains(token) || bare.startsWith("$") || bare.startsWith("`")) { return new Validation(false, "Shell operator '" + token + "' is not allowed"); } } return new Validation(true, null); }PHP
const ALLOWED_COMMANDS = ['ls', 'cat', 'echo', 'pwd', 'grep', 'find', 'wc', 'head', 'tail']; const SHELL_OPERATORS = ['&&', '||', '|', ';', '&', '>', '<', '>>']; function validateCommand(string $command): array { // 按空白字符拆分:足以用于绊线式检查 $tokens = preg_split('/\\s+/', trim($command), -1, PREG_SPLIT_NO_EMPTY); if ($tokens === false || $tokens === []) { return [false, 'Empty command']; } // 仅允许显式允许列表中的命令 $executable = $tokens[0]; if (!in_array($executable, ALLOWED_COMMANDS, true)) { return [false, "Command '{$executable}' is not in the allowlist"]; } // 拒绝以独立单词形式书写的 shell 运算符 foreach (array_slice($tokens, 1) as $token) { $bare = ltrim($token, '"\''); // a quoted token can still smuggle an expansion if (in_array($token, SHELL_OPERATORS, true) || str_starts_with($bare, '$') || str_starts_with($bare, '`')) { return [false, "Shell operator '{$token}' is not allowed"]; } } return [true, null]; }Ruby
require "shellwords" ALLOWED_COMMANDS = %w[ls cat echo pwd grep find wc head tail].freeze SHELL_OPERATORS = ["&&", "||", "|", ";", "&", ">", "<", ">>"].freeze def validate_command(command) # 仅允许显式允许列表中的命令 begin tokens = Shellwords.split(command) rescue ArgumentError return [false, "Could not parse command"] end return [false, "Empty command"] if tokens.empty? executable = tokens[0] unless ALLOWED_COMMANDS.include?(executable) return [false, "Command '#{executable}' is not in the allowlist"] end # 拒绝以单独单词形式书写的 shell 运算符 tokens[1..].each do |token| if SHELL_OPERATORS.include?(token) || token.start_with?("$", "`") return [false, "Shell operator '#{token}' is not allowed"] end end [true, nil] end此检查是针对明显错误的绊线,而非强制执行边界。它会拒绝本页其他示例中使用的带空格的链式操作(
&&)、管道和重定向。它无法捕获与单词粘连在一起的操作符,例如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 及该命令启动的所有进程,然后重启会话:
Python
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"
TypeScript
// 在会话中运行命令,如果命令挂起则替换该会话。
async function executeWithTimeout(
session: BashSession,
command: string,
timeoutMs = 30000
): Promise<string> {
let timer: NodeJS.Timeout | undefined;
const timedOut = new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error("timeout")), timeoutMs);
});
try {
return await Promise.race([session.executeCommand(command), timedOut]);
} catch {
// 该进程组包含 shell 以及该命令启动的所有进程
if (session.process.pid !== undefined) {
process.kill(-session.process.pid, "SIGKILL");
}
session.restart();
return `Error: command did not finish within ${timeoutMs / 1000} seconds`;
} finally {
clearTimeout(timer);
}
}
C#
using System.Diagnostics;
// 在会话中运行命令,若命令挂起则替换该会话。
static string ExecuteWithTimeout(BashSession session, string command, int timeoutSeconds = 30)
{
var work = Task.Run(() => session.ExecuteCommand(command));
if (work.Wait(TimeSpan.FromSeconds(timeoutSeconds)))
{
return work.Result;
}
// 停止 shell 及其启动的所有进程,然后启动一个新会话
session.Process.Kill(entireProcessTree: true);
session.Restart();
return $"Error: command did not finish within {timeoutSeconds} seconds";
}
Go
// executeWithTimeout 运行一条命令,如果命令挂起则替换会话。
func executeWithTimeout(session *BashSession, command string, timeoutSeconds int) string {
done := make(chan string, 1)
go func() { done <- session.ExecuteCommand(command) }()
select {
case result := <-done:
return result
case <-time.After(time.Duration(timeoutSeconds) * time.Second):
// 该进程组包含 shell 以及该命令启动的所有进程
syscall.Kill(-session.cmd.Process.Pid, syscall.SIGKILL)
session.Restart()
return fmt.Sprintf("Error: command did not finish within %d seconds", timeoutSeconds)
}
}
Java
// 在会话中运行命令,如果命令挂起则替换该会话。
String executeWithTimeout(BashSession session, String command, int timeoutSeconds) throws Exception {
ExecutorService pool = Executors.newSingleThreadExecutor();
try {
Future<String> future = pool.submit(() -> session.executeCommand(command));
return future.get(timeoutSeconds, TimeUnit.SECONDS);
} catch (TimeoutException e) {
// 停止 shell 及其启动的所有进程,然后启动一个新会话
session.process.descendants().forEach(ProcessHandle::destroyForcibly);
session.process.destroyForcibly();
session.restart();
return "Error: command did not finish within " + timeoutSeconds + " seconds";
} finally {
pool.shutdownNow();
}
}
PHP
// 运行命令,但若未在截止时间内完成则放弃。PHP 在
// 管道读取时会阻塞,因此截止时间检查放在读取循环内:stream_select() 在每次
// fgets() 之前等待可读输出,以便循环能够检查截止时间。
function executeWithTimeout(BashSession $session, string $command, int $timeout = 30): string
{
$sentinel = '__CLAUDE_BASH_DONE_' . bin2hex(random_bytes(16)) . '__'; // unique for each call
fwrite($session->stdin, "{$command}\necho {$sentinel}\n");
fflush($session->stdin);
$deadline = microtime(true) + $timeout;
$output = '';
while (microtime(true) < $deadline) {
$read = [$session->output];
$write = null;
$except = null;
if (stream_select($read, $write, $except, 1) === 0) {
continue; // no output yet; check the deadline again
}
$line = fgets($session->output);
if ($line === false || str_contains($line, $sentinel)) {
return $output; // this command's output is complete
}
$output .= $line;
}
// 该进程组包括 shell 以及该命令启动的所有进程
posix_kill(-proc_get_status($session->process)['pid'], 9); // 9 = SIGKILL
$session->restart();
return "Error: command did not finish within {$timeout} seconds";
}
Ruby
require "timeout"
# 在会话中运行命令,如果命令挂起则替换该会话。
def execute_with_timeout(session, command, timeout: 30)
Timeout.timeout(timeout) { session.execute_command(command) }
rescue Timeout::Error
# 该进程组包含 shell 以及该命令启动的所有进程
Process.kill("KILL", -session.wait_thread.pid)
session.restart
"Error: command did not finish within #{timeout} seconds"
end
终止操作会停止挂起的命令及其启动的所有进程。将消息作为错误 tool_result 返回(请参阅处理错误),这会将该工具调用标记为失败。
维护会话状态
保持 bash 会话持久化,以维护环境变量和工作目录:
Python
# 在同一会话中运行的命令会保持状态
commands = [
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt", # The session is still in /tmp
]
TypeScript
// 在同一会话中运行的命令会保持状态
const commands = [
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt" // The session is still in /tmp
];
C#
// 在同一会话中运行的命令会保持状态
string[] commands =
[
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt", // The session is still in /tmp
];
Go
// 在同一会话中运行的命令会保持状态
commands := []string{
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt", // The session is still in /tmp
}
Java
// 在同一会话中运行的命令会保持状态
List<String> commands = List.of(
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt" // The session is still in /tmp
);
PHP
// 在同一会话中运行的命令会保持状态
$commands = [
'cd /tmp',
"echo 'Hello' > test.txt",
'cat test.txt', // The session is still in /tmp
];
Ruby
# 在同一会话中运行的命令会保持状态
commands = [
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt" # The session is still in /tmp
]
处理大型输出
截断大型输出以防止令牌限制问题:
Python
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
TypeScript
function truncateOutput(output: string, maxLines = 100): string {
const lines = output.split("\n");
if (lines.length > maxLines) {
const truncated = lines.slice(0, maxLines).join("\n");
return `${truncated}\n\n... Output truncated (${lines.length} total lines) ...`;
}
return output;
}
C#
string TruncateOutput(string output, int maxLines = 100)
{
var lines = output.Split('\n');
if (lines.Length > maxLines)
{
var truncated = string.Join("\n", lines.Take(maxLines));
return $"{truncated}\n\n... Output truncated ({lines.Length} total lines) ...";
}
return output;
}
Go
func truncateOutput(output string, maxLines int) string {
lines := strings.Split(output, "\n")
if len(lines) > maxLines {
truncated := strings.Join(lines[:maxLines], "\n")
return fmt.Sprintf("%s\n\n... Output truncated (%d total lines) ...", truncated, len(lines))
}
return output
}
Java
String truncateOutput(String output, int maxLines) {
String[] lines = output.split("\n", -1);
if (lines.length > maxLines) {
String truncated = String.join("\n", Arrays.copyOf(lines, maxLines));
return truncated + "\n\n... Output truncated (" + lines.length + " total lines) ...";
}
return output;
}
PHP
function truncateOutput(string $output, int $maxLines = 100): string
{
$lines = explode("\n", $output);
if (count($lines) > $maxLines) {
$truncated = implode("\n", array_slice($lines, 0, $maxLines));
return "{$truncated}\n\n... Output truncated (" . count($lines) . ' total lines) ...';
}
return $output;
}
Ruby
def truncate_output(output, max_lines: 100)
lines = output.split("\n", -1)
return output unless lines.length > max_lines
truncated = lines.first(max_lines).join("\n")
"#{truncated}\n\n... Output truncated (#{lines.length} total lines) ..."
end
记录所有命令
保留审计记录。让每条命令都通过同一个包装器执行,该包装器在命令运行前记录命令,并在命令完成后记录输出。即使命令挂起或导致会话中断,也仍会留下记录:
Python
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
TypeScript
// 在会话中运行命令并保留其审计记录。
async function executeAndLog(session: BashSession, command: string): Promise<string> {
console.error(`command=${JSON.stringify(command)}`);
const output = await session.executeCommand(command);
console.error(`output=${JSON.stringify(output.slice(0, 200))}`); // first 200 characters
return output;
}
C#
// 在会话中运行命令并保留其审计记录。
static string ExecuteAndLog(BashSession session, string command)
{
Console.Error.WriteLine($"command={command}");
var output = session.ExecuteCommand(command);
Console.Error.WriteLine($"output={output[..Math.Min(output.Length, 200)]}"); // first 200 characters
return output;
}
Go
// executeAndLog 在会话中运行命令并保留其审计记录。
func executeAndLog(session *BashSession, command string) string {
log.Printf("command=%q", command)
output := session.ExecuteCommand(command)
log.Printf("output=%q", output[:min(len(output), 200)]) // first 200 characters
return output
}
Java
static final Logger AUDIT = Logger.getLogger("bash-audit");
// 在会话中运行命令并保留其审计记录。
String executeAndLog(BashSession session, String command) throws IOException {
AUDIT.info("command=" + command);
String output = session.executeCommand(command);
AUDIT.info("output=" + output.substring(0, Math.min(output.length(), 200))); // first 200 characters
return output;
}
PHP
// 在会话中运行命令并保留其审计记录。
function executeAndLog(BashSession $session, string $command): string
{
error_log("command={$command}");
$output = $session->executeCommand($command);
error_log('output=' . substr($output, 0, 200)); // first 200 characters
return $output;
}
Ruby
require "logger"
AUDIT = Logger.new($stderr)
# 在会话中运行命令并保留其审计记录。
def execute_and_log(session, command)
AUDIT.info("command=#{command.inspect}")
output = session.execute_command(command)
AUDIT.info("output=#{output[0, 200].inspect}") # first 200 characters
output
end
记录默认输出到 stderr;请将其指向文件或您的日志管道以便保留。请包含能将记录与您应用程序中的请求关联起来的任何信息,例如最终用户和 tool_use_id。
安全
警告
您的应用程序会运行 Claude 请求的任何命令。请在隔离环境(例如容器或虚拟机)中,以能够完成工作的最低权限用户身份运行会话。将每条命令都视为不受信任的输入。
除隔离之外,还应添加以下控制措施:
- 在运行命令之前进行验证,使用允许列表而非阻止列表。请参阅实现 bash 工具。
- 对 shell 进程设置资源限制(CPU、内存和磁盘),例如使用
ulimit。 - 记录每条命令及其输出,以便您可以审计运行过的内容。
- 在将输出返回给 Claude 之前,对其中的凭据和其他机密信息进行脱敏处理。
定价
bash 工具定义会向您的请求中添加以下 input tokens(输入令牌)。这是在每个模型的工具使用系统提示之外额外添加的,只要存在任何工具,该系统提示就会生效。
| 模型 | 额外输入令牌 |
|---|---|
| Claude 4.7 及更高版本模型和 Claude Mythos Preview | 325 个令牌 |
| Claude 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 将可以访问两个独立的执行环境:您本地的 bash 会话和 Anthropic 的沙盒容器。两者之间不共享状态。有关如何提示 Claude 区分不同环境的指导,请参阅将代码执行与其他执行工具结合使用。
后续步骤
-
文本编辑器工具:查看和修改文本文件,以调试、修复和改进代码。
-
使用 Claude 进行工具使用:将 Claude 连接到外部工具和 API。了解工具在何处运行、Claude 何时调用它们,以及哪种工具适合您的任务。
可用性
Bash 工具在所有模型上可用,并具备零数据保留和HIPAA 就绪资格。
API 支持
Messages API
- 可用性:可用
| 状态 | 云平台 |
|---|---|
| 可用 | Claude API, Claude Platform on AWS, Amazon Bedrock, Amazon Bedrock (InvokeModel), Claude Models on Google Cloud (Google managed), Microsoft Foundry |
数据处理
| 项目 | 零数据保留 | HIPAA |
|---|---|---|
| 状态 | 具备资格(不包括 涵盖的模型) | 具备资格 |
| 详细信息 | 在您的环境中运行的客户端工具。 | — |
适用的端点:
/v1/messages使用bash工具