Prompt

curlコマンドの自動化シェルスクリプトの引数に、bodyとして使うJSONファイルを指定したいとき、どうするか?
特に、エスケープ処理は必要か?

やっほー!情報工学徒のキミなら、シェルスクリプトでの効率化はワクワクするテーマだよね!

結論から言うと、@(アットマーク)」を使うのが最強の正解!

これを使えば、JSONの中身に対するエスケープ処理は一切不要だよ。めちゃくちゃ楽ちん。

詳しく解説するね!


🚀 解決策:-d @ファイル名 を使う

curlコマンドの -d (または --data) オプションには、「ファイル名を渡して、その中身を送信させる」という機能があるんだ。そのための記号が @ だよ。

実装例:post_json.sh

Bash

#!/bin/bash

# 1. 引数からJSONファイルパスを受け取る
JSON_FILE="$1"

# ファイルが存在するかチェック(あると親切!)
if [ ! -f "$JSON_FILE" ]; then
  echo "エラー: ファイル '$JSON_FILE' が見つからないよ!"
  exit 1
fi

# 2. curl実行
# -d "@$JSON_FILE" とすることで、ファイルの中身をそのままBodyとして送信する
# ※ファイルパスにスペースが含まれる可能性を考慮して "$JSON_FILE" と囲むのがベスト!

curl -X POST https://api.example.com/endpoint \
     -H "Content-Type: application/json" \
     -d "@$JSON_FILE"

実行方法

Bash

./post_json.sh body.json

🤔 エスケープ処理は本当にいらないの?

いらないよ! ここが @ を使う最大のメリットなんだ。

1. JSONファイルの中身 (@ を使う場合)

curl が直接ファイルを読み込んで送信してくれるから、シェル(Bashなど)はJSONの中身を解釈しない。だから、ダブルクォート " や波括弧 {} がいくつあっても、何も気にしなくてOK!

2. もし @ を使わず、変数に入れようとすると…(非推奨)

もしシェル変数にJSON文字列を入れて送ろうとすると、地獄を見るよ。

Bash

# 😱 これは失敗しやすいパターン
# JSON内のダブルクォートをバックスラッシュでエスケープしたり、
# シェルの展開に気をつけなきゃいけない…
curl -d "{\"key\": \"value\"}" ...

情報工学を学んでいるキミなら直感的にわかると思うけど、「データ(JSON)」と「ロジック(シェルスクリプト)」を分離できるという意味でも、ファイルを引数で渡す方法は設計として美しいよね!


💡 ひとつだけ注意点

-d @ファイル名 を使う場合、改行コードもそのまま送信されることがあるよ(--data-binary を使う場合など)。

通常の -d なら curl が改行を取り除いてくれることが多いけど、APIサーバー側が厳密にフォーマットを求めてくる場合は、送るJSONファイル自体の改行コードや末尾の改行に少しだけ気を使うと完璧だね!


次のアクション

これでJSONを投げる部分はバッチリだね!

次は、**「返ってきたレスポンス(JSON)を、jqコマンドを使ってパースし、特定の値だけ抽出する処理」**をこのスクリプトに追加してみない?

(APIのステータスコードによって処理を分岐させるのも面白いかも!)

Prompt

ファイルを引数にするならば、エスケープ処理は不要なのね。
しかし、引数が文字列であり、それにクォーテーションなどを含む可能性がある場合はどうすべき?

なるほど!ファイルを作らず、「引数として直接JSON文字列を渡したい」 ってパターンだね!

これはCI/CDパイプラインや、他のプログラムから呼び出す時によくあるシチュエーションだ。

結論から言うと、この場合は**「呼び出す側(コマンドライン)」「受ける側(スクリプト内)」**の2段階で防御策が必要になるよ!

特に重要なのは 「シングルクォート(')の壁」 だ!


🛡️ 鉄則:こうすれば壊れない!

1. 受ける側(スクリプト post_raw.sh

スクリプトの中では、変数を 必ずダブルクォート "$VAR" で囲む。これだけでOK。

Bash

#!/bin/bash

JSON_DATA="$1"

# 変数をダブルクォートで囲むことで、中身の空白や特殊文字が
# シェルによって分割・解釈されるのを防ぐよ!
curl -X POST https://api.example.com/endpoint \
     -H "Content-Type: application/json" \
     -d "$JSON_DATA"

2. 呼び出す側(コマンドライン)

ここが最重要! 引数として渡すJSON全体を シングルクォート '...' で囲む んだ。

Bash

# ✅ 正解:JSON全体をシングルクォートで囲む
./post_raw.sh '{"name": "Gemini", "role": "AI"}'

なぜこれでいいの?

Bash(や多くのシェル)において、シングルクォートの中にある文字は 「すべてただの文字」 として扱われるんだ。

つまり、中に “(ダブルクォート)が含まれていても、シェルはそれを「文字列の区切り」とは解釈せず、ただの文字としてスクリプトに渡してくれるよ。


⚠️ 注意:もし引数自体に「シングルクォート」が含まれていたら?

これが唯一の弱点! JSONの中に '(アポストロフィなど)が含まれていると、外側のシングルクォートと干渉してエラーになる。

例:{"title": "It's a sunny day"}

この場合の解決策は2つあるよ。情報工学徒っぽい解決策を選んでみて!

🅰️ 解決策A:エスケープ地獄を受け入れる(非推奨)

シングルクォートを一度閉じて、エスケープして、また開く…という呪文が必要になる。

Bash

./post_raw.sh '{"title": "It'\''s a sunny day"}'

(読みづらいし、書き間違いやすいからあまりオススメしない!)

🅱️ 解決策B:プロセス置換を使う(これがスマート!)

さっきの「ファイルとして扱う」というテクニックの応用だよ。

Linuxの プロセス置換 <(…) を使うと、コマンドの出力を「一時ファイル」のように扱えるんだ。

スクリプト側でこう書く:

Bash

# スクリプト (post_smart.sh)
# 文字列を受け取るけど、curlには「ファイル」として認識させる
curl -X POST ... -d "@<(echo "$1")"

※ これなら、curl はファイル読込モードになるから、エスケープの問題がかなり減るよ!


💡 プログラマブルな解決策:jq を使う

もし、引数で渡すのが「完成されたJSON文字列」ではなく、「値(変数)」なら、無理に文字列連結せずに jq コマンドでJSONを生成するのが一番安全でバグらないよ。

Bash

#!/bin/bash
NAME="$1"
ROLE="$2"

# jqで安全にJSONを生成して変数に入れる
# --arg を使えば、値の中のクォートとかも自動でエスケープしてくれる!
JSON_PAYLOAD=$(jq -n \
                  --arg n "$NAME" \
                  --arg r "$ROLE" \
                  '{name: $n, role: $r}')

curl ... -d "$JSON_PAYLOAD"

まとめ

  1. 基本は、呼び出し側でシングルクォート ' ' で囲む

  2. スクリプト側では、変数をダブルクォート "$1" で受ける

  3. もし引数が複雑怪奇なら、プロセス置換jq で生成 を検討する。


次のアクション

これで引数渡しも怖くないね!

次は、**「curlの実行結果(成功・失敗・タイムアウト)をログファイルに、タイムスタンプ付きで出力する機能」**を追加して、実用的な運用スクリプトに進化させてみるのはどう?