現場で使えるシェルスクリプト入門18:curlコマンドによるWeb API連携とデータ取得の基本

シェルスクリプト IT

前回の記事(入門17)では、バッチ処理や夜間のcronジョブなどでスクリプトが重ねて実行されるのを防ぐ「二重起動防止(排他制御)」と、trapコマンドを用いた例外時の自動復旧手順について解説しました。

今回は、現代のシステム開発や運用自動化において不可欠な、Web APIや外部Webサーバーと通信してデータを取得・送信するための超定番コマンド「curl(カール)」の基本的な使い方を解説します。

入門16で解説したJSONパースツール jqcurl を組み合わせることで、天気予報や株価、社内WebシステムのAPIからデータを自動取得し、通知や集計を行う強力なシェルスクリプトを構築できるようになります!

curlコマンドとは?

curl(Client for URL)は、コマンドラインからURLを指定してデータ転送を行うための高機能な通信ツールです。HTTP、HTTPS、FTPなどの様々なプロトコルに対応しており、WebページのHTML取得からWeb APIの呼び出しまで幅広く利用されます。

多くのLinuxディストリビューションやmacOSには標準でインストールされていますが、未導入の場合は以下の管理コマンドでインストールできます。

  • macOS(Homebrew)の場合brew install curl
  • Ubuntu/Debianの場合sudo apt-get install curl
  • Red Hat系の場合sudo dnf install curl(または sudo yum install curl

curlコマンドを使用したWeb API取得のサンプルスクリプト

テスト用の公開Web API(JSONPlaceholder)からサンプルデータ(ToDoタスク情報)を curl で取得し、jq コマンドでタイトルとステータスを取り出して表示する実践的なシェルスクリプト(fetch_api.sh)を作成しました。

#!/bin/bash

# スクリプトを安全に実行するための基本設定
set -euo pipefail

# 1. 接続先のWeb APIエンドポイントURLを定義
API_URL="https://jsonplaceholder.typicode.com/todos/1"

echo "--- Web APIからのデータ取得を開始します ---"

# 2. curl コマンドを使ってAPIからJSONレスポンスを取得し、変数に格納する
# 💡 -s (サイレントモード) を指定して余計な進捗メーターを出力させないようにします
RESPONSE_JSON=$(curl -s "$API_URL")

# 3. 取得したJSONデータが空でないことを確認
if [ -z "$RESPONSE_JSON" ]; then
    echo "エラー: Web APIからのデータ取得に失敗しました。" >&2
    exit 1
fi

# 4. jq コマンドを使って目的のフィールドを取り出す
TASK_TITLE=$(echo "$RESPONSE_JSON" | jq -r '.title')
IS_COMPLETED=$(echo "$RESPONSE_JSON" | jq -r '.completed')

# 5. 抽出したデータを出力する
echo "タスク名: $TASK_TITLE"
echo "完了ステータス: $IS_COMPLETED"

echo "処理が正常に完了しました。"

実行結果:

$ ./fetch_api.sh
--- Web APIからのデータ取得を開始します ---
タスク名: delectus aut autem
完了ステータス: false
処理が正常に完了しました。

プログラムコードの詳細解説

上記のスクリプトで使われている主要なポイントを解説します。

  • curl -s オプション(サイレントモード)
    curl -s "$API_URL"-s(または --silent)オプションは、通常ターミナルに出力されるダウンロードの進捗状況(プログレスメーター)を非表示にする設定です。シェル変数へ出力を代入する際は、このオプションを指定しないと余計な文字が進捗データとして紛れ込んでしまうため必須となります。
  • コマンド置換 $( … ) によるレスポンス受け取り
    RESPONSE_JSON=$(curl -s "$API_URL") と書くことで、Web APIから返ってきたJSON文字列全体を直接シェル変数 RESPONSE_JSON に格納しています。
  • jqコマンドとの組み合わせパース
    取得した変数データを echo "$RESPONSE_JSON" | jq -r '.title' のようにパイプで jq に渡すことで、必要なデータのみを抽出し、別の変数に分けて安全に利用できます。

curlコマンドの基本的な使い方と主要オプション

シェルスクリプト内で活用するために、curl コマンド単体での基本的なリクエスト方法と実務でよく使われるオプションを整理しておきましょう。

GETリクエストによるデータ取得(基本)

最も基本となる使い方です。指定したURLのデータ(HTMLやJSONなど)を取得して画面(標準出力)に表示します。

# URLを指定してデータを取得する
$ curl https://example.com/api/data

サイレントモードとエラー表示(-s, -S オプション)

-s オプションは進捗表示を消しますが、通信エラーが発生した際のエラーメッセージも隠れてしまいます。進捗を消しつつ、エラーが起きた時だけエラーメッセージを表示させたい場合は -sS(または -s -S)と組み合わせるのがおすすめです。

# 進捗表示を消し、エラー発生時のみメッセージを出力する
$ curl -sS https://example.com/api/data

HTTPヘッダーの指定(-H オプション)

Web APIへリクエストを送る際、データの形式(Content-Type)や認証トークン(Authorization)を送信する場合は -H オプションを使用します。

# JSON形式としてアクセスし、Bearerトークンを送る例
$ curl -sS -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_API_TOKEN" \
          https://example.com/api/v1/user

POSTリクエストによるデータ送信(-X POST, -d オプション)

Web APIに対してデータを新規登録・送信する場合は、リクエストメソッドを -X POST で指定し、送信するボディデータを -d(または --data)オプションで指定します。

# POSTメソッドでJSONデータを送信する例
$ curl -sS -X POST \
          -H "Content-Type: application/json" \
          -d '{"name": "Tokyo", "temp": 25.5}' \
          https://example.com/api/v1/weather

通信のメタ情報を出力する(-w / –write-out オプション)

-w(または --write-out)オプションは、通信が完了した後に、通信に関する各種メタ情報(HTTPステータスコード、レスポンスタイム、ダウンロードサイズなど)を指定したフォーマットで出力させる機能です。

%{変数名} の形式で取得したい情報を指定します。代表的な変数には以下のようなものがあります。

  • %{response_code}(旧名: %{http_code}):レスポンスのステータスコード(例: 200, 404, 500)。古くから使われている http_code も互換性のため動作しますが、現在のcurl公式ではより汎用的な response_code が推奨されています。
  • %{time_total}:リクエスト開始から転送完了までの総所要時間(秒)
  • %{size_download}:ダウンロードしたレスポンスのサイズ(バイト)

例えば、レスポンスボディを破棄(-o /dev/null)し、ステータスコードの数字だけを取得したい場合は、-w "%{response_code}" を組み合わせて以下のように記述します。

# レスポンス本文を捨てる(-o /dev/null)で、ステータスコードだけを取得する
$ HTTP_STATUS=$(curl -s -o /dev/null -w "%{response_code}" https://example.com/api/data)
$ echo "ステータスコード: $HTTP_STATUS"
# 出力例: ステータスコード: 200

シェルスクリプト連携時の落とし穴とエラーハンドリング

シェルスクリプト内で curl を使用する際、特に気をつけるべき「2つの落とし穴」とその対策を解説します。

① -s オプションを忘れ、変数内に進捗が出力混入する落とし穴

前述の通り、RESPONSE=$(curl "$URL") のように -s オプションを付けずにコマンド置換で変数に代入した場合、プログレスメーター(パーセンテージ表示等)が標準エラー出力から漏れ出たり、出力結果を汚してしまう原因になります。

② 404や500エラーの際、curl自体は「成功(0)」扱いになる落とし穴

これが最もハマりやすい落とし穴です。curl コマンドは、「Webサーバーとの通信自体が成功した場合、たとえサーバーが 404 Not Found や 500 Internal Server Error のエラー画面を返してきても、終了ステータス 0(成功)を返す」という仕様になっています。

そのため、スクリプトの冒頭に set -e を記述していても、404エラーで止まらずに後続処理が進んでしまいます。

この仕様への対策として、以下の2つの方法があります。

  • 対策1:-f(–fail)オプションを付ける
    curl -fsS "$URL" のように -f オプションを指定すると、HTTPステータスコードが 400 以上の場合に curl 自体が非0のエラーを返して即時停止するようになります。
  • 対策2:1回の通信でステータスコードと本文データを同時に取得して分岐する
    curlの -w "\n%{response_code}" オプションを使用すると、1回のリクエストでレスポンス本文の末尾にステータスコードを付与して取得できます。これをシェルスクリプト側で「ステータスコード」と「本文データ」に分離して判定することで、通信回数を1回に抑えつつ安全にエラー分岐が可能です。

対策1の例(-f オプションで 404/500 エラーを検知する例):

# -f オプションを付けて 404/500 エラーを検知する例
if ! RESPONSE=$(curl -fsS "$API_URL"); then
    echo "エラー: HTTPエラーが発生しました。" >&2
    exit 1
fi

対策2の例(ステータスコードと本文データを同時に受け取って判定する):

# 1回の通信で本文と、最終行にステータスコードを付与して取得する(%{response_code} を推奨)
RESPONSE_FULL=$(curl -sS -w "\n%{response_code}" "$API_URL")

# 最終行からステータスコードのみを取り出す
HTTP_STATUS=$(echo "$RESPONSE_FULL" | tail -n 1)

# 最終行以外(レスポンス本文データ)を取り出す
RESPONSE_BODY=$(echo "$RESPONSE_FULL" | sed '$d')

# ステータスコードが 200 OK 以外の場合はエラー終了する
if [ "$HTTP_STATUS" -ne 200 ]; then
    echo "エラー: 異常なステータスコードが返されました (HTTP $HTTP_STATUS)" >&2
    exit 1
fi

# 正常時は取得したレスポンス本文を利用する
echo "取得成功: $RESPONSE_BODY"

このテクニックのポイントは以下の通りです。

  • -w “\n%{response_code}” の役割
    レスポンス本文を出力した直後に、改行(\n)を挟んでステータスコードの数字(200や404など)を追記して取得します。
  • tail -n 1 によるステータスコードの抽出
    tail -n 1 を使用して、変数データの「最後の1行」だけを切り出すことで、純粋なステータスコードの数値を取り出します。
  • sed ‘$d’ による本文データの抽出
    sed '$d' は「最終行を削除する」コマンドです。これにより、末尾に追加されたステータスコードの行だけを取り除き、元々のレスポンス本文(JSONなど)だけを抽出できます。

まとめ

今回は、シェルスクリプトでWeb APIや外部サーバーと通信するための必須コマンド「curl」の基本的な使い方から、実務で使えるオプション設定、エラーハンドリングの注意点について解説しました。

curl でデータを取得し、jq でパースし、trapset -euo pipefail で安全に保護する組み合わせを身につけることで、実務における自動化スクリプトを作成できるようになります。ぜひマスターしてください!

コメント

タイトルとURLをコピーしました