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"で受ける。 -
もし引数が複雑怪奇なら、プロセス置換 か
jqで生成 を検討する。
次のアクション
これで引数渡しも怖くないね!
次は、**「curlの実行結果(成功・失敗・タイムアウト)をログファイルに、タイムスタンプ付きで出力する機能」**を追加して、実用的な運用スクリプトに進化させてみるのはどう?