Bash 도구
Claude가 셸 명령을 요청하면 애플리케이션이 지속적인 bash 세션에서 이를 실행하고 도구 결과로 반환하도록 합니다.
bash 도구는 클라이언트 도구입니다. Claude는 명령을 직접 실행하지 않습니다. 요청에 이 도구를 포함하면 Claude는 실행할 명령을 지정하는 tool_use 블록으로 응답합니다. 애플리케이션은 자체적으로 소유한 bash 세션에서 해당 명령을 실행하고 출력을 tool_result 블록으로 반환합니다.
애플리케이션은 도구 호출 전반에 걸쳐 하나의 bash 프로세스를 유지하므로 명령 간에 상태가 지속됩니다. 작업 디렉터리, 환경 변수, 그리고 명령이 생성한 모든 파일은 다음 명령에서도 그대로 남아 있습니다.
이 도구의 현재 버전은 bash_20250124입니다. 모델 지원, 베타 헤더 및 이전 버전에 대해서는 도구 버전을 참조하세요. 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는 상태를 저장하지 않습니다(stateless). 셸 세션에 관한 어떤 것도 요청 간에 전달되지 않으므로, 세션이 언제 시작되고 얼마나 오래 유지되며 언제 재시작할지는 애플리케이션이 결정합니다. 전체 요청 및 응답 주기에 대해서는 도구 호출 처리를 참조하세요.
매개변수
bash 도구 정의에는 type과 name이라는 두 개의 필수 필드가 있으며, name은 반드시 bash여야 합니다. 이 도구는 스키마가 없습니다. 스키마가 Claude 모델에 내장되어 있어 수정할 수 없으므로 input_schema를 제공하지 않습니다. 다음 표는 Claude가 도구를 호출할 때 설정하는 입력 필드를 나열합니다.
| 매개변수 | 필수 | 설명 |
|---|---|---|
command | 예* | 실행할 bash 명령 |
restart | 아니요 | bash 세션을 재시작하려면 true로 설정 |
*restart를 사용하지 않는 한 필수
restart: true를 처리하려면 셸 프로세스를 종료하고 새 프로세스를 시작한 다음, 재시작을 확인하는 tool_result를 반환하세요. 재시작된 세션은 깨끗한 상태로 시작됩니다. 작업 디렉터리, 환경 변수 및 실행 중이던 모든 프로세스가 사라집니다.
명령 실행:
{
"command": "ls -la *.py"
}세션 재시작:
{
"restart": true
}도구 버전
bash_20250124는 이 도구의 현재 버전이며 베타 헤더가 필요하지 않습니다. Claude Sonnet 3.7(종료) 이후의 모든 모델이 이를 지원하며, 현재의 모든 Claude 모델도 포함됩니다.
원래의 bash_20241022 버전은 2024년 10월 Claude Sonnet 3.5 모델(종료)에서만 작동합니다. 이를 사용하는 요청에는 anthropic-beta: computer-use-2024-10-22 헤더가 필요하며, SDK는 베타 네임스페이스에서만 이를 노출합니다. 새로운 통합에서는 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는 어떤 명령을 실행할지 결정합니다. 그 외의 모든 것, 즉 셸 프로세스, 타임아웃, 안전 검사는 애플리케이션이 담당합니다. 다음 단계는 최소한의 구현을 보여줍니다.
지속적인 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과 섞어 출력하므로 오류 메시지가 발생한 위치에 나타납니다. 이 예시에는 완전한 구현에 필요한 것이 빠져 있습니다. 명령이 멈췄을 때 셸과 셸이 시작한 모든 프로세스를 종료한 다음 세션을 재시작하는 타임아웃입니다. 명령 타임아웃 사용 모범 사례에서 이를 추가하는 한 가지 방법을 보여줍니다.
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 하나씩, 모두 다음 user 메시지에 담아 반환 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인 동안 실행 및 반환 주기를 반복하세요. 전체 루프에 대해서는 클라이언트 도구의 결과 처리하기를 참조하세요.안전 조치 구현하기
검증과 제한을 추가하세요. 차단 목록(blocklist) 대신 허용 목록(allowlist)을 사용하세요. 차단 목록은 예상하지 못한 명령을 모두 놓치게 됩니다. 이 예시는 별도의 단어로 나타나는 셸 연산자도 거부합니다:
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" # 별도의 단어로 작성된 셸 연산자는 거부합니다 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
}
]
}구현 모범 사례 따르기
입력을 기다리는 명령처럼 끝나지 않는 명령은 센티널 줄이 도착하지 않기 때문에 세션을 영원히 차단합니다. 모든 명령에 기한을 부여하세요. 기한이 지나면 셸과 명령이 시작한 모든 것을 중지한 다음 세션을 재시작하세요:
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:
# 그룹은 셸과 해당 명령이 시작한 모든 프로세스를 포함합니다
os.killpg(session.process.pid, signal.SIGKILL)
session.restart()
return f"Error: command did not finish within {timeout} seconds"kill은 멈춘 명령과 그 명령이 시작한 모든 것을 중지합니다. 메시지를 오류 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 도구 구현을 참조하세요.
- 예를 들어
ulimit을 사용하여 셸 프로세스에 리소스 제한(CPU, 메모리, 디스크)을 설정하세요. - 무엇이 실행되었는지 감사할 수 있도록 모든 명령과 그 출력을 로깅하세요.
- Claude에 반환하기 전에 출력에서 자격 증명 및 기타 비밀 정보를 삭제하세요.
가격
bash 도구 정의는 요청에 다음과 같은 입력 토큰을 추가합니다. 이는 도구가 하나라도 존재할 때마다 적용되는 모델별 도구 사용 시스템 프롬프트에 추가되는 것입니다.
| 모델 | 추가 입력 토큰 |
|---|---|
| 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 세션 상태는 클라이언트 측에 있습니다. 턴 사이에 셸 세션을 유지하는 것은 애플리케이션의 책임입니다.
- 출력 한도: API는 도구 결과를 잘라내지 않습니다(크기가 초과된 요청은 거부됩니다). Claude에 반환하기 전에 애플리케이션에서 대용량 출력을 잘라내세요.
- 스트리밍 불가: 출력은 애플리케이션이 다음 요청에서
tool_result를 반환할 때에만 Claude에 도달합니다.
다른 도구와 결합
bash 도구는 텍스트 편집기 도구와 잘 어울립니다. Claude는 한 도구로 파일을 편집하고 다른 도구로 이를 실행하는 명령을 요청합니다.
다음 단계
코드를 디버그하고 수정하고 개선하기 위해 텍스트 파일을 보고 수정합니다.
Claude를 외부 도구 및 API에 연결합니다. 도구가 어디서 실행되는지, Claude가 언제 도구를 호출하는지, 어떤 도구가 작업에 적합한지 알아보세요.
Was this page helpful?