アルアカ - Arcadia Academia

Arcadia Academiaは「エンジニアリングを楽しむ」を合言葉に日本のデジタル競争力を高めることをミッションとするテックコミュニティです。

curlでURLの角括弧がエラーになる原因と--globoffでの直し方

Featured image of the post

「curl で API を叩いたら、返ってくるのが JSON じゃない」 「同じ URL をブラウザで開くと普通に表示されるのに、curl だけ結果が違う」 「エラーは後段の Python が吐く JSONDecodeError。API が壊れたと思って半日調べていた」

こういうとき、犯人が API でもネットワークでもなく curl 自身だったというケースがあります。curl は URL に含まれる角括弧を「複数 URL への展開(グロブ)」として読むため、配列添字を使うクエリを投げると、こちらが書いたのとは違うリクエストになってしまうのです。

この記事では、なぜそうなるのか、どう見分けるのか、そして1文字のオプションでどう直すのかを、そのままコピーできるコマンド付きでまとめます。

💡
30秒で要点
curl は URL 中の角括弧を「範囲指定」、波括弧を「列挙」として解釈し、複数のリクエストに展開する
そのため配列添字を使う API のクエリ(apps[0].app=201 のような形)が壊れる
エラーは curl ではなく後段の JSON パースで出ることが多く、原因が見えにくい
対処は -g--globoff)を足すだけ。API 用の curl は curl -sg を既定形にする
シェルの引用符では防げない。展開しているのはシェルではなく curl 自身

目次を開く

curl の URL は「1本」とは限らない

curl には、1つの URL を複数のリクエストに展開する機能があります。使うのは2種類の記号です。

# 波括弧=列挙。3リクエストに展開される
curl -O "https://example.com/{alpha,beta,gamma}.txt"

# 角括弧=範囲。001 から 010 まで 10リクエストに展開される
curl -O "https://example.com/img[001-010].png"

連番のファイルをまとめて落とすときには便利な機能です。問題は、この展開をしているのがシェルではなく curl 自身だという点にあります。シングルクォートで囲んでもダブルクォートで囲んでも、curl まで届いた角括弧は curl が展開します。ここが最大の誤解ポイントです。

詰まりの実例:配列添字を含む API クエリ

多くの Web API は、配列パラメータを name[0]=value の形で受け取ります。kintone・GitHub・PHP や Rails 系のフォーム送信などで広く使われている書き方です。

curl "$BASE/k/v1/preview/app/deploy.json?apps[0].app=201"

curl から見ると、この角括弧は範囲指定です。中身が範囲として解釈できなければリクエストを送る前にエラーになり、解釈できてしまう形なら別の URL に展開されて送られます。どちらにせよ、こちらが意図した1本のリクエストにはなりません。

なぜ「API が壊れた」と誤診するのか

スクリプトの中では、curl の出力をそのまま次の処理に渡していることがほとんどです。

curl -s "$URL" | python3 -c "import sys, json; print(json.load(sys.stdin))"

-s を付けている以上、curl 側のエラーは画面に出ません。出力は JSON ではないので、落ちるのは後段の Python で、メッセージは json.decoder.JSONDecodeError になります。curl という文字がエラーのどこにも出てこないため、「API のレスポンスが変わった」「認証が切れた」と見当違いの方向を調べることになります。

直し方:-g--globoff)を足す

グロブ機能を切るオプションが用意されています。

curl -g "$BASE/k/v1/preview/app/deploy.json?apps[0].app=201"

短縮形の -g と長い形の --globoff は同じ意味です。実務では -s(進捗を出さない)と合わせて、次の形を定型にしておくと踏まなくなります。

curl -sg -H "X-Cybozu-API-Token: $TOKEN" "$URL"

API を叩く curl は `-sg` から書き始める、と決めてしまうのが一番確実です。

切り分け:-v で「実際に送られた URL」を見る

疑わしいときは、リクエストの中身を目で確認します。

curl -gv "$URL" -o /dev/null

出力の中の GET /k/v1/... で始まる行が、自分の書いたパス・クエリと一致しているかを見ます。ここがズレていれば、原因は API より手前にあります。

見えるもの 判断 次の手
-g を外すとエラー、付けると通る グロブ展開が原因で確定 -sg を定型にする
どちらでも同じレスポンスが返る curl は無関係 認証ヘッダ・パラメータ名を疑う
送信行のクエリが自分の書いた形と違う 手前で URL が加工されている 変数展開・パス連結を確認する

角括弧を残したまま通す:パーセントエンコード

-g を使わずに済ませたい場合は、角括弧そのものをエンコードしてしまう手もあります。角括弧の開きは %5B、閉じは %5D です。

curl "$BASE/k/v1/preview/app/deploy.json?apps%5B0%5D.app=201"

これならグロブの対象になりません。ただし読みづらく、パラメータが増えるほど間違えやすくなるので、常用するなら -g のほうが素直です。

併用できない点に注意

-g はグロブ機能そのものを無効化します。つまり、同じコマンドで連番ダウンロードは使えません。連番取得と API 呼び出しは別のコマンドに分けてください。オプションの詳しい説明は、手元の版の curl --helpman curl で確認できます。

まとめ

  • curl は URL 中の角括弧を範囲、波括弧を列挙として自分で展開する
  • そのため配列添字を使う API のクエリが壊れ、症状は後段の JSON パースエラーとして現れる
  • シェルの引用符では防げない。展開しているのは curl 自身
  • 対処は -g--globoff)。API 用の curl は curl -sg を既定形にしておく
  • 迷ったら -v を付けて、実際に送られたリクエスト行を目で見るのが最短

エラーメッセージが原因と噛み合わない種類の詰まりなので、一度踏んでおくと次から数分で片付きます。

よくある質問(FAQ)

シェルの引用符で囲めば防げますか?

防げません。角括弧を展開しているのはシェルではなく curl 自身なので、シングルクォートでもダブルクォートでも結果は変わりません。防ぐには -g を付けるか、角括弧をパーセントエンコードします。

-g を付けると連番ダウンロードが使えなくなりますか?

なります。-g はグロブ機能全体を切るオプションなので、範囲指定や列挙も一緒に無効になります。連番でファイルを落とす処理と、API を叩く処理は別のコマンドに分けるのが安全です。

POST のボディに角括弧が入っている場合も壊れますか?

グロブが働くのは URL の部分だけです。-d--data で渡すボディは影響を受けません。壊れるのは、パスやクエリ文字列に角括弧を書いたときです。

wget や Python の requests でも同じことが起きますか?

起きません。URL のグロブ展開は curl 側の機能です。「ブラウザや他のクライアントでは通るのに curl でだけ結果がおかしい」という状況なら、まずこれを疑う価値があります。

プログラミング学習でお悩みですか?

現役エンジニアがあなたの学習をマンツーマンでサポートします。

  • 学習の進め方がわからない
  • ポートフォリオの作り方を知りたい
  • 現場で使える技術を学びたい
まずは30分の無料相談

相談は完全無料・オンラインで気軽に

あなたを爆速で成長させるメンタリングプログラムはこちら

メンタープログラムバナー

学習・開発のお悩みは現役エンジニアに相談

メンタープログラムの詳細を見る

エンジニアの基礎学習ゲーム

プログラミング学習支援

無料相談はこちら