Bash-Tool
Lass Claude Shell-Befehle anfordern, die deine Anwendung in einer persistenten Bash-Sitzung ausführt und als Tool-Ergebnisse zurückgibt.
Das Bash-Tool ist ein Client-Tool: Claude führt Befehle nicht selbst aus. Wenn du das Tool in eine Anfrage aufnimmst, antwortet Claude mit einem tool_use-Block, der den auszuführenden Befehl benennt. Deine Anwendung führt diesen Befehl in einer Bash-Sitzung aus, die ihr gehört, und gibt die Ausgabe in einem tool_result-Block zurück.
Deine Anwendung hält einen Bash-Prozess über Tool-Aufrufe hinweg am Leben, sodass der Zustand zwischen Befehlen erhalten bleibt. Das Arbeitsverzeichnis, Umgebungsvariablen und alle Dateien, die ein Befehl erstellt, sind für den nächsten Befehl noch vorhanden.
Die aktuelle Version des Tools ist bash_20250124. Informationen zur Modellunterstützung, zu Beta-Headern und zur früheren Version findest du unter Tool-Versionen. Alle von Anthropic bereitgestellten Tools findest du in der Tool-Referenz.
Anwendungsfälle
- Entwicklungs-Workflows: Build-Befehle, Tests und Entwicklungstools ausführen
- Systemautomatisierung: Skripte ausführen, Dateien verwalten, Aufgaben automatisieren
- Datenverarbeitung: Dateien verarbeiten, Analyseskripte ausführen, Datensätze verwalten
- Umgebungseinrichtung: Pakete installieren, Umgebungen konfigurieren
Schnellstart
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 antwortet mit stop_reason: "tool_use" und einem tool_use-Block, der den Befehl enthält, den deine Anwendung ausführen soll:
{
"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"
}
}
]
}Führe input.command in deiner Bash-Sitzung aus und sende die Ausgabe als tool_result zurück. Den vollständigen Roundtrip findest du unter Das Bash-Tool implementieren.
So funktioniert es
Jeder Tool-Aufruf ist ein Roundtrip zwischen Claude und deiner Anwendung:
- Claude gibt einen
tool_use-Block zurück, der den auszuführendencommandenthält. - Deine Anwendung führt den Befehl in ihrer Bash-Sitzung aus.
- Deine Anwendung gibt die Ausgabe des Befehls, stdout und stderr zusammen, in einem
tool_result-Block an Claude zurück. - Claude fordert entweder einen weiteren Befehl in derselben Sitzung an oder antwortet mit Text.
Claude kann auch mehrere tool_use-Blöcke in einer Antwort zurückgeben. Führe sie der Reihe nach in derselben Sitzung aus und gib alle Ergebnisse in einer einzigen user-Nachricht zurück. Siehe Parallele Tool-Nutzung.
Die API ist zustandslos. Nichts über deine Shell-Sitzung wird zwischen Anfragen übertragen, daher entscheidet deine Anwendung, wann die Sitzung beginnt, wie lange sie lebt und wann sie neu gestartet wird. Den vollständigen Anfrage- und Antwortzyklus findest du unter Tool-Aufrufe verarbeiten.
Parameter
Eine Bash-Tool-Definition hat zwei Pflichtfelder, type und name, und name muss bash sein. Das Tool ist schemalos: Du gibst kein input_schema an, da das Schema in Claudes Modell eingebaut ist und nicht geändert werden kann. Die folgende Tabelle listet die Eingabefelder auf, die Claude setzt, wenn es das Tool aufruft.
| Parameter | Erforderlich | Beschreibung |
|---|---|---|
command | Ja* | Der auszuführende Bash-Befehl |
restart | Nein | Auf true setzen, um die Bash-Sitzung neu zu starten |
*Erforderlich, sofern nicht restart verwendet wird
Um restart: true zu verarbeiten, beende den Shell-Prozess, starte einen neuen und gib ein tool_result zurück, das den Neustart bestätigt. Eine neu gestartete Sitzung beginnt sauber: Das Arbeitsverzeichnis, Umgebungsvariablen und alle laufenden Prozesse sind weg.
Einen Befehl ausführen:
{
"command": "ls -la *.py"
}Die Sitzung neu starten:
{
"restart": true
}Tool-Versionen
bash_20250124 ist die aktuelle Version des Tools und erfordert keinen Beta-Header. Jedes Modell ab Claude Sonnet 3.7 (eingestellt) akzeptiert sie, einschließlich aller aktuellen Claude-Modelle.
Die ursprüngliche Version bash_20241022 funktioniert nur mit dem Claude Sonnet 3.5-Modell vom Oktober 2024 (eingestellt). Anfragen, die sie verwenden, benötigen den Header anthropic-beta: computer-use-2024-10-22, und die SDKs stellen sie nur in ihren Beta-Namespaces bereit. Neue Integrationen sollten bash_20250124 verwenden.
Beispiel: Mehrstufige Automatisierung
Claude kann Befehle über Tool-Aufrufe hinweg verketten, um eine mehrstufige Aufgabe zu erledigen:
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"}Die Sitzung behält den Zustand zwischen Befehlen bei, sodass in Schritt 2 erstellte Dateien in Schritt 3 verfügbar sind.
Das Bash-Tool implementieren
Claude bestimmt, welcher Befehl ausgeführt wird. Deine Anwendung ist für alles andere verantwortlich: den Shell-Prozess, das Timeout und die Sicherheitsprüfungen. Die folgenden Schritte zeigen eine minimale Implementierung.
Eine persistente Bash-Sitzung erstellen
Starte einen langlebigen Bash-Prozess und führe jeden Befehl darin aus. Da eine Pipe zu einem laufenden Prozess niemals ein Dateiende meldet, gibt die Sitzung nach jedem Befehl eine eindeutige Sentinel-Zeile aus, um zu markieren, wo die Ausgabe dieses Befehls endet:
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 stateDie Sitzung verschränkt stderr mit stdout, sodass Fehlermeldungen dort landen, wo sie aufgetreten sind. Das Beispiel lässt aus, was eine vollständige Implementierung ebenfalls benötigt: ein Timeout, das die Shell und jeden von ihr gestarteten Prozess beendet, wenn ein Befehl hängt, und dann die Sitzung neu startet. Die Best Practice Befehls-Timeouts verwenden zeigt eine Möglichkeit, dies hinzuzufügen.
Claudes Tool-Aufrufe verarbeiten
Extrahiere Befehle aus Claudes Antworten und führe sie aus:
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) # Ein tool_result pro tool_use-Block, alle in der nächsten User-Nachricht zurückgegeben tool_results.append( {"type": "tool_result", "tool_use_id": content.id, "content": result} )Das Ergebnis an Claude zurückgeben
Sende das
tool_resultin eineruser-Nachricht zurück, die dieselbe Konversation fortsetzt. Claude fordert entweder einen weiteren Befehl in derselben Sitzung an oder schließt seine Antwort ab: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)Wiederhole den Zyklus aus Ausführen und Zurückgeben, solange
stop_reasonden Werttool_usehat. Die vollständige Schleife findest du unter Ergebnisse von Client-Tools verarbeiten.Sicherheitsmaßnahmen implementieren
Füge Validierung und Einschränkungen hinzu. Verwende eine „allowlist" (Positivliste) statt einer „blocklist" (Sperrliste): Eine Blocklist übersieht jeden Befehl, den sie nicht vorhergesehen hat. Das Beispiel lehnt außerdem Shell-Operatoren ab, die als separate Wörter auftreten:
import shlex ALLOWED_COMMANDS = {"ls", "cat", "echo", "pwd", "grep", "find", "wc", "head", "tail"} SHELL_OPERATORS = {"&&", "||", "|", ";", "&", ">", "<", ">>"} def validate_command(command): # Erlaube nur Befehle aus einer expliziten Allowlist 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" # Lehne Shell-Operatoren ab, die als separate Wörter geschrieben sind for token in tokens[1:]: if token in SHELL_OPERATORS or token.startswith(("$", "`")): return False, f"Shell operator '{token}' is not allowed" return True, NoneDiese Prüfung ist ein Stolperdraht für offensichtliche Fehler, keine Durchsetzungsgrenze. Sie lehnt die mit Leerzeichen getrennte Verkettung (
&&), Pipes und Umleitungen ab, die die anderen Beispiele auf dieser Seite verwenden. Sie erkennt keinen Operator, der an ein Wort geklebt ist, wie etwacat data.txt|grep x, da der Tokenizerdata.txt|grepinnerhalb eines Tokens belässt. Entscheide, welche Befehle und Operatoren deine Anwendung zulässt. Die eigentliche Kontrolle ist Isolation: Führe die gesamte Sitzung in einem Container oder einer virtuellen Maschine aus (siehe Sicherheit).
Fehler behandeln
Wenn ein Befehl fehlschlägt oder die Sitzung abbricht, teile Claude mit, was passiert ist. Gib die Meldung als tool_result-Inhalt zurück und setze is_error auf true, was den Tool-Aufruf als fehlgeschlagen markiert. Siehe Fehlerbehandlung mit is_error.
Wenn die Ausführung eines Befehls zu lange dauert:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Error: command did not finish within 30 seconds",
"is_error": true
}
]
}Wenn ein Befehl nicht existiert:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "bash: nonexistentcommand: command not found",
"is_error": true
}
]
}Wenn es Berechtigungsprobleme gibt:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "bash: /root/sensitive-file: Permission denied",
"is_error": true
}
]
}Best Practices für die Implementierung befolgen
Ein Befehl, der nie endet, etwa einer, der auf Eingabe wartet, blockiert die Sitzung für immer, weil seine Sentinel-Zeile nie ankommt. Gib jedem Befehl eine Frist. Wenn die Frist abläuft, stoppe die Shell und alles, was der Befehl gestartet hat, und starte dann die Sitzung neu:
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:
# Die Gruppe umfasst die Shell und jeden Prozess, den der Befehl gestartet hat
os.killpg(session.process.pid, signal.SIGKILL)
session.restart()
return f"Error: command did not finish within {timeout} seconds"Der Kill stoppt den hängenden Befehl und alles, was er gestartet hat. Gib die Meldung als Fehler-tool_result zurück (siehe Fehler behandeln), was den Tool-Aufruf als fehlgeschlagen markiert.
Halte die Bash-Sitzung persistent, um Umgebungsvariablen und das Arbeitsverzeichnis beizubehalten:
# Befehle, die in derselben Sitzung ausgeführt werden, behalten ihren Zustand bei
commands = [
"cd /tmp",
"echo 'Hello' > test.txt",
"cat test.txt", # The session is still in /tmp
]Kürze große Ausgaben, um Probleme mit Token-Limits zu vermeiden:
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 outputFühre ein Audit-Protokoll. Leite jeden Befehl durch einen Wrapper, der den Befehl vor der Ausführung und die Ausgabe nach Abschluss aufzeichnet. Ein Befehl, der hängt oder die Sitzung abbricht, hinterlässt trotzdem einen Eintrag:
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 outputDie Einträge gehen standardmäßig an stderr; leite sie in eine Datei oder deine Logging-Pipeline, um sie aufzubewahren. Nimm alles auf, was den Eintrag in deiner Anwendung mit der Anfrage verknüpft, etwa den Endnutzer und die tool_use_id.
Sicherheit
Füge über die Isolation hinaus diese Kontrollen hinzu:
- Validiere Befehle vor der Ausführung, mit einer Allowlist statt einer Blocklist. Siehe Das Bash-Tool implementieren.
- Setze Ressourcenlimits für den Shell-Prozess (CPU, Arbeitsspeicher und Festplatte), zum Beispiel mit
ulimit. - Protokolliere jeden Befehl und seine Ausgabe, damit du prüfen kannst, was ausgeführt wurde.
- Schwärze Zugangsdaten und andere Geheimnisse in der Ausgabe, bevor du sie an Claude zurückgibst.
Preise
Die Bash-Tool-Definition fügt deiner Anfrage die folgenden Input-Token hinzu. Dies gilt zusätzlich zum modellspezifischen System-Prompt für die Tool-Nutzung, der immer dann angewendet wird, wenn ein beliebiges Tool vorhanden ist.
| Modell | Zusätzliche Input-Token |
|---|---|
| Claude Opus 5, Claude Opus 4.8 und Claude Opus 4.7 | 325 Token |
| Claude Opus 4.6, Claude Sonnet 4.6 und früher | 244 Token |
Zusätzliche Token werden verbraucht durch:
- Befehlsausgaben (stdout/stderr)
- Fehlermeldungen
- Große Dateiinhalte
Vollständige Preisdetails findest du unter Preise für Tool-Nutzung.
Häufige Muster
Entwicklungs-Workflows
- Tests ausführen:
pytest && coverage report - Projekte bauen:
npm install && npm run build - Git-Operationen:
git status && git add . && git commit -m "message"
Hinweise zur Verwendung von git als Checkpoint- und Wiederherstellungsmechanismus in lang laufenden Agenten-Workflows findest du unter Best Practices für die Zustandsverwaltung.
Dateioperationen
- Daten verarbeiten:
wc -l *.csv && ls -lh *.csv - Dateien durchsuchen:
find . -name "*.py" | xargs grep "pattern" - Backups erstellen:
tar -czf backup.tar.gz ./data
Systemaufgaben
- Ressourcen prüfen:
df -h && free -m - Prozessverwaltung:
ps aux | grep python - Umgebungseinrichtung:
export PATH=$PATH:/new/path && echo $PATH
Einschränkungen
- Keine interaktiven Befehle: Die Sitzung kann
vim,less, Passwortabfragen oder andere Befehle, die auf Eingabe über stdin warten, nicht ausführen. - Keine GUI-Anwendungen: Die Sitzung ist rein kommandozeilenbasiert.
- Sitzungsumfang: Der Zustand der Bash-Sitzung liegt clientseitig. Deine Anwendung ist dafür verantwortlich, die Shell-Sitzung zwischen den Turns aufrechtzuerhalten.
- Ausgabelimits: Die API kürzt Tool-Ergebnisse nicht (eine zu große Anfrage wird abgelehnt). Kürze große Ausgaben in deiner Anwendung, bevor du sie an Claude zurückgibst.
- Kein Streaming: Die Ausgabe erreicht Claude erst, wenn deine Anwendung das
tool_resultin der nächsten Anfrage zurückgibt.
Kombination mit anderen Tools
Das Bash-Tool lässt sich gut mit dem Texteditor-Tool kombinieren: Claude bearbeitet eine Datei mit dem einen Tool und fordert mit dem anderen den Befehl an, der sie ausführt.
Nächste Schritte
Textdateien anzeigen und ändern, um Code zu debuggen, zu korrigieren und zu verbessern.
Verbinde Claude mit externen Tools und APIs. Erfahre, wo Tools ausgeführt werden, wann Claude sie aufruft und welches Tool zu deiner Aufgabe passt.
Was this page helpful?