Claude Media
Claude Codeをシェルループで回して複数ファイルを一括変更する

Claude Codeをシェルループで回して複数ファイルを一括変更する

claude -pをシェルのforループで回し、数百〜数千ファイルへ同じ変更を機械的に適用する手順。--allowedToolsによる権限の絞り込みとリトライ設定まで扱います。

Claude Codeをシェルループで回すとはどういうことか

対象ファイルを1本ずつ挙げ、claude -p(非対話モード)をforループで呼び出し、同じ指示を機械的に適用していく手法です。Claude Codeには、コードベース全体の変更を5〜30の独立した単位に分けて並列実行する/batchがあります。この記事は、シェル側のループとClaude Codeの非対話実行を組み合わせて自分で組み立てる形を扱います。

対象は数百〜数千ファイル規模の一括変換です。フレームワークの移行、命名規則の統一、非推奨APIの置き換えのように「やることは同じだが対象が大量にある」作業に向きます。1ファイルごとに文脈が独立しているため、前のファイルで書いた変更が次のファイルの判断に影響しません。これは弱点にも強みにもなります。

いつシェルループを使い、いつ他の手段を使うか

Claude Codeで複数ファイルを扱う方法は1つではありません。同じ「複数」でも、独立性の高さと構築コストで向き不向きが分かれます。

手段実行単位向く場面
シェルループ(claude -p)実行単位ファイルごとに独立したCLIプロセス向く場面数百〜数千件の機械的な一括変換、CI組み込み
サブエージェント(Agent tool)実行単位1セッション内の子コンテキスト向く場面調査・実装・検証を分業したい少数タスク
/batch実行単位単位ごとのバックグラウンドサブエージェントとworktree(5〜30単位、計画の承認あり、gitリポジトリかworktreeを作るWorktreeCreateフックが必要)向く場面コードベース全体にまたがる大きな変更を、Claude Codeに分解から任せたい
worktree並列(複数セッション)実行単位独立したgitチェックアウト向く場面依存のある実装を人が指揮して並行させたい
Agent Teams実行単位独立コンテキスト+相互通信向く場面チームメイト同士が発見を共有・議論する必要がある調査

シェルループの強みは単純さです。ループ本体はただのbashで、Claude Code側に特別な設定は要りません。裏返すと、ファイル間の依存を考慮した判断はできません。「このファイルの変更に合わせて別のファイルも直す」ような相互参照が必要な移行には向きません。

その場合はサブエージェントで1セッションに文脈をまとめるか、Claude Codeオーケストレーター設計にあるような指揮型の並列実装が適します。人が各セッションの進行を見ながら並行させたいならClaude Code Worktree実践ガイドの隔離手法のほうが向きます。ファイル単位の独立性が高い一括変換だけがシェルループの得意分野です。

3ステップで組み立てる

ステップ1: 対象ファイルの一覧を作る

最初にClaude Code自身へ対象を洗い出させ、ファイルに保存します。

Vueへの移行が必要な2,000件のファイルを列挙し、
files.txtに保存してください。

一覧を先に固定しておくことで、次のループスクリプトはただそれを読むだけで済みます。対象選定の判断とループ実行を分離する狙いです。

ステップ2: ループスクリプトを書く

files.txtを1行ずつ読み、ファイルごとにclaude -pを呼び出します。

while IFS= read -r file; do
  claude -p "Migrate $file from React to Vue. Return OK or FAIL." \
    --allowedTools "Edit,Bash(git commit *)" \
    --max-turns 20 --max-budget-usd 1.00 \
    --no-session-persistence \
    || echo "$file" >> failed.txt
done < files.txt

for file in $(cat files.txt)と書く定番の形は、パスに空白があると1つのファイルが複数の引数に割れます。while readで1行ずつ読めば、空白入りのパスもそのまま渡せます。

--allowedToolsは確認プロンプトなしで使わせるツールを絞り込むフラグです。ここではEditとプレフィックス一致のBash(git commit *)のみを許可し、それ以外のBashコマンドは許可リストに入っていないため、そのまま実行されることはありません。開始モードによって、拒否されるか自動判定に回ります。無人実行では、この絞り込みが暴走を防ぐ実質的な安全弁になります。

プロンプトの最後に「Return OK or FAIL」と結果形式を指定しているのは、ループ側で成否を機械的に判定できるようにするためです。止め方の安全弁も入れてあります。--max-turnsは上限に達するとclaudeがエラーで終了し、||の後ろでそのファイルがfailed.txtに残ります。--max-budget-usdは1回の呼び出しで使う金額の上限です。一覧の1行が暴走しても、被害は1ファイル分の上限で止まります。

ステップ3: 数件でテストしてから全体を回す

いきなり2,000件を流さず、最初の2〜3件で結果を確認してから本番を回します。プロンプトの指示が曖昧だと、最初の数件でずれた変更が入り、残り全部に同じずれが波及します。

たとえば「Reactから移行」とだけ書いた最初の指示が、状態管理のライブラリまで一緒に置き換えてしまったとします。ここで全件に流す前に気づければ、直す範囲はテスト対象の2〜3件だけで済みます。プロンプトに「状態管理ライブラリはそのまま残す」と明記し、同じ2〜3件で再確認してから本番のファイル数へ広げます。この往復を惜しむと、2,000件流し終えた後に同じ手戻りをファイル単位でやり直すことになります。

個別ツール指定と権限モード、どちらでスコープを絞るか

権限の絞り込み方には--allowedToolsのほかにもう1つ選択肢があります。セッション全体の基準を--permission-modeで決める方法です。

-pの開始モードは環境で変わります。フィーチャーフラグを取得するセッションではManual(設定値はdefault)で始まり、確認が必要な操作は実行されません。サードパーティープロバイダー経由やテレメトリー無効のセッションでは、v2.1.285以降はautoで始まります。ループ用途で現実的なのは次の2つです。

くらべる

ループで使う2つの権限モード

書き込みを通す

acceptEdits

ファイル書き込みとmkdir・touch・mv・cp・rm・rmdir・sedといった基本的なファイル操作は、確認なしで進みます。ループでrmやsedも通る点に注意が要ります。それ以外のシェルコマンドやネットワークアクセスは、--allowedToolsかpermissions.allowルールが必要です。

許可済み以外を拒否

dontAsk

permissions.allowルールか読み取り専用コマンド以外は一律で拒否します。ロックダウンしたCI実行に向きます。AskUserQuestionと、requiresUserInteraction指定のMCPツールは、許可ルールに一致していても拒否されます。

ファイル1本ごとに許可するツールが変わらないなら、--allowedToolsで個別に列挙するほうが見通しがよくなります。「書き込みは許すがそれ以外は全部止める」という一律の基準で運用したいなら、--permission-modeで先にベースラインを決め、必要な差分だけ--allowedToolsで足します。

公式のCI向け例も同じ形です。--permission-mode dontAskに--allowedTools "Bash(npm test)" "Read"を添え、許可を完全一致のリストで渡します。

毎回の起動コストを減らす--bare

--bareなしのclaude -pは、対話セッションと同じコンテキストを読み込みます。作業ディレクトリや~/.claudeにあるhooks、skills、サブエージェント、プラグイン、MCPサーバー、自動メモリ、CLAUDE.mdが対象です。数千回のループでは、この読み込みが起動のたびに積み上がります。

--bareを付けるとこれらの自動探索を飛ばし、スクリプト呼び出しの起動が速くなります。公式はスクリプトやSDKの呼び出しに--bareを勧めており、将来のリリースで-pの既定にするとしています。

注意点が2つあります。まず、--bareではOAuth認証情報もシステムのキーチェーンも読みません。認証にはANTHROPIC_API_KEYかapiKeyHelperを使います。次に、CLAUDE.mdも読まれなくなるため、命名規則のような共通指示は、プロンプトか--append-system-promptで渡し直します。

v2.1.289のclaude --helpでは、予算と保存に関する2つのフラグが次のように表示されます。

$ claude --version
2.1.289 (Claude Code)
$ claude --help
  --max-budget-usd <amount>             Maximum dollar amount to spend on API
                                        calls (only works with --print)
  --no-session-persistence              Disable session persistence - sessions
                                        will not be saved to disk and cannot be
                                        resumed (only works with --print)

どちらも--print(-p)専用のフラグです。対話セッションに付けても効きません。

落とし穴 — 無人実行だからこそ起きる失敗

シェルループは対話的な確認が入らない分、途中の異常に人が気づきにくくなります。実行前に押さえておきたい4点です。

注意

無人ループの4つの落とし穴

  • 429/529でループが早期に止まる

    API側が混雑すると429(利用上限)や529(過負荷)のエラーが返ります。既定のリトライ回数は10回で、CLAUDE_CODE_MAX_RETRIESで上げても上限は15回です。CI・夜間バッチのように長時間の無人実行では、環境変数CLAUDE_CODE_RETRY_WATCHDOG=1を設定します。429/529を無制限にリトライし、サーバーエラーやタイムアウトの上限も300回に上がります。使用量の上限なら、リセットまで待ちます。

  • 許可を広げすぎる

    Bashを無条件で許可すると、意図しないコマンドまで確認なしで実行されます。ステップ2のBash(git commit *)のように、必要なコマンドだけを通します。*の前の空白が肝心で、Bash(git commit*)と書くと別のコマンド名にも一致します。

  • セッションが積み上がる

    claude -pはデフォルトで再開可能なセッションを保存します。数千回のループを流すとセッションが積み上がるため、後から参照する必要がなければ--no-session-persistenceで保存自体を止めます。

  • 成否だけでは直っているか分からない

    プロンプトが返す成否は「エラーなく終わったか」であって「意図通りに直っているか」ではありません。ビルドやテストが通る変更かどうかは、ループとは別に検証を挟みます。

支出上限の429は例外です。CLAUDE_CODE_RETRY_WATCHDOGを有効にしていても、v2.1.239以降は上限に達した時点で即座に失敗します。ループが予算で止まったときは、リトライではなく上限の引き上げを検討する場面です。

失敗したファイルだけを再実行する

ステップ2のスクリプトは、エラー終了したファイルをfailed.txtに残します。claudeが正常に終わっても返答がFAILのファイルを拾うには、出力を変数で受けて判定する形に変えます。

手順

失敗分だけ回し直す流れ

  1. 1

    結果を1行ずつ記録する

    --output-format jsonで受け取り、jq -r '.result'で本文を取り出します。本文がOKで始まらなければfailed.txtへ追記し、total_cost_usdも同じJSONから拾ってコストを並べて残します。

    while IFS= read -r file; do
      out=$(claude -p "Migrate $file from React to Vue. Return OK or FAIL." \
        --output-format json --no-session-persistence \
        --allowedTools "Edit,Bash(git commit *)") \
        || { echo "$file" >> failed.txt; continue; }
      printf '%s\t%s\n' "$file" "$(jq -r '.total_cost_usd' <<<"$out")" >> cost.tsv
      [[ "$(jq -r '.result' <<<"$out")" == OK* ]] || echo "$file" >> failed.txt
    done < files.txt
  2. 2

    `failed.txt`を入力にして回し直す

    done < files.txtの読み込み先をfailed.txtに替えて、同じループを流します。成功済みのファイルは対象に入らないので、二重に処理されません。

  3. 3

    残った失敗はプロンプトを見直す

    同じファイルが何度も落ちるなら、再実行を重ねるより、そのファイルだけ対話セッションで原因を見るほうが早く片付きます。

デバッグ中は--verboseを足して詳細ログを見て、本番では外す運用にすると、ログの量を必要な場面だけに絞れます。

まとめ

シェルループは、独立したファイルへ同じ変更を大量にかける用途に絞った道具です。依存の判断が要る変更はサブエージェントやオーケストレーターに任せます。無人で長時間走らせるなら、権限スコープ、リトライ設定、1回あたりの予算上限の3点を実行前に決めておくと、手戻りが小さく済みます。

よくある質問

ループを並列実行に変えることはできますか

シェル側で&によるバックグラウンド実行やxargs -Pを使えば、同時実行数を増やせます。その分APIのレート制限に早く当たりやすくなり、同じファイルへの同時コミットで衝突する可能性も出ます。依存のないファイルの読み取り専用処理から試すのが安全です。

コストはどう見積もればよいですか

--output-format jsonで返るtotal_cost_usdはクライアント側の推定額で、実際の請求額とは差が出ることがあります。数件のテスト実行で1件あたりのコストを確認し、対象件数を掛けて概算する進め方が現実的です。概算をもとに--max-budget-usdを決めておけば、1回あたりの上限も設計できます。

CLAUDE.mdの規約はループの各呼び出しでも読まれますか

--bareを付けない限り、読まれます。CLAUDE.mdはセッション開始時に毎回読み込まれる仕組みなので、claude -pをループで2,000回呼べば2,000回読み込まれます。プロジェクトの命名規則やコーディング規約をCLAUDE.mdに書いておけば、都度プロンプトに書き足さなくても各ファイルの変換に反映されます。

逆に、ループ専用の指示(「このファイルだけは対象外」等)はCLAUDE.mdに書かず、都度のプロンプトかファイル一覧側で制御するほうが、他のセッションへの影響を避けられます。

CI(GitHub Actions等)に組み込めますか

組み込めます。非対話実行そのものはCI・pre-commitフック・自動化パイプラインへの統合を想定した機能で、GitHub Actions上での具体的な設定はClaude CodeをGitHub Actionsに組み込むで扱っています。CIでは--bareを付けて、ホストのhooksやCLAUDE.mdを読み込ませない形が公式の例です。

この記事を共有:XはてブLinkedIn