DevOps Bootcamp · 课时

结合 curl 与 jq 调用 REST API

在脚本中将 curl 请求传入 jq,以提取、分页处理并重新格式化实时 API 响应。

第 3 / 4 课13 个步骤

结合 curl 与 jq 调用 REST API 是 CoddyKit 上的免费 DevOps Bootcamp 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 DevOps Bootcamp 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 DevOps Bootcamp 课程共包含 4 节课。

为什么 curl + jq 是强大组合

REST API 返回 JSON。curl 获取原始响应;jq 对其进行切片、过滤和重塑——全部在一个流水线中完成。不需要 Python 脚本、Postman,也不需要中间文件。

  • curl 处理 HTTP:方法、标头、身份验证和重定向。
  • jq 处理 JSON:过滤、映射、转换和格式化。
  • 将二者通过管道连接起来,就能创建简洁且可组合的 API 工作流。

本课将从基础原理出发,通过真实的分页和脚本模式来培养这项技能。

将基本 curl 流水线传入 jq

最简单的模式:将 curl 输出直接通过管道传入 jq。使用 -s(静默)来隐藏 curl 的进度指示器,使只有 JSON 正文传递给 jq。

  • -s — 静默模式,不显示进度条。
  • . — jq 的恒等过滤器;以易读格式输出完整响应。
  • jq 的 -r — 原始输出(字符串不带外围引号)。
#!/usr/bin/env bash
# Fetch a public endpoint and pretty-print the JSON
curl -s 'https://jsonplaceholder.typicode.com/todos/1' | jq '.'

# Extract just the title field as a plain string
curl -s 'https://jsonplaceholder.typicode.com/todos/1' | jq -r '.title'

设置请求标头并传递身份验证令牌

大多数生产环境 API 都要求提供 Authorization 标头或 API 密钥。使用 -H 传递标头,并将机密信息存储在环境变量中——绝不要将其硬编码。

  • -H 'Authorization: Bearer $TOKEN' — 注入身份验证标头。
  • -H 'Accept: application/json' — 明确请求返回 JSON。
  • 变量会在双引号内展开;请使用 " 包裹标头字符串。
#!/usr/bin/env bash
TOKEN="${GITHUB_TOKEN}"   # set in your shell environment
USER="octocat"

curl -s \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Accept: application/vnd.github+json" \
  "https://api.github.com/users/${USER}" \
| jq '{login: .login, repos: .public_repos, followers: .followers}'

过滤数组 — .[] 和 select()

API 经常返回数组。使用 .[] 遍历每个元素,然后使用 select() 只保留符合条件的项目。

  • .[] — 将数组展开为对象流。
  • select(.field == value) — 只保留匹配的对象。
  • 使用 | 连接多个过滤器。
#!/usr/bin/env bash
# Fetch all todos and keep only the completed ones
curl -s 'https://jsonplaceholder.typicode.com/todos' \
| jq '[.[] | select(.completed == true) | {id, title}]'

# Count how many are completed
curl -s 'https://jsonplaceholder.typicode.com/todos' \
| jq '[.[] | select(.completed == true)] | length'

使用 map() 提取多个字段

map() 会对数组中的每个元素应用转换,并返回一个新数组——功能等同于 [.[] | ...],但可读性更好。

  • map({key: .field}) — 重塑每个对象。
  • 与 @csv 或 @tsv 结合,生成表格数据。
  • 将 -r 与 @csv/@tsv 搭配使用,可获取原始文本(不进行 JSON 引号处理)。
#!/usr/bin/env bash
# Reshape a posts list into id + title pairs
curl -s 'https://jsonplaceholder.typicode.com/posts' \
| jq 'map({id, title: .title[0:40]})'

# Output as CSV for import into a spreadsheet
curl -s 'https://jsonplaceholder.typicode.com/posts' \
| jq -r '.[] | [.id, .userId, .title] | @csv'

分页模式——循环直到页面为空

大多数接口都会对结果进行分页。一种常见模式是使用 while 循环递增页码计数器,并在返回的数组为空时停止。

  • 使用 $(curl ...) 将 curl 响应保存到变量中。
  • 使用 jq 'length' 检查页面是否包含项目。
  • 使用 jq -s(汇总)累积结果,或将结果追加到文件中。
#!/usr/bin/env bash
# Paginate through jsonplaceholder posts (simulated: page stops at page 2
# because the API returns the full list regardless of ?_page, but the
# pattern is correct for real paginated APIs)

PAGE=1
PER_PAGE=10
OUTPUT="all_posts.json"
echo '[]' > "$OUTPUT"

while true; do
  RESPONSE=$(curl -s "https://jsonplaceholder.typicode.com/posts?_page=${PAGE}&_limit=${PER_PAGE}")
  COUNT=$(echo "$RESPONSE" | jq 'length')

  if [ "$COUNT" -eq 0 ]; then
    echo "No more pages. Stopping at page $((PAGE - 1))."
    break
  fi

  # Merge new items into the accumulated JSON array
  CURRENT=$(cat "$OUTPUT")
  echo "$CURRENT" "$RESPONSE" | jq -s '.[0] + .[1]' > "$OUTPUT"
  echo "Page $PAGE: fetched $COUNT items."
  PAGE=$((PAGE + 1))
done

echo "Total collected: $(jq 'length' "$OUTPUT")"

链接标头分页(GitHub 风格)

GitHub 和许多其他接口会使用 Link 响应标头提供下一页的 URL。您必须解析该标头,而不是猜测 URL。

  • curl -i 会将响应标头包含在标准输出中;也可以使用 -D - 将标头转储到标准输出。
  • 使用 grep 和 sed 解析 Link: <url>; rel="next" 标头。
  • 循环执行,直到不存在 rel="next" 链接。
#!/usr/bin/env bash
# Follow Link-header pagination (GitHub repos example)
# Requires GITHUB_TOKEN in environment
TOKEN="${GITHUB_TOKEN}"
NEXT_URL="https://api.github.com/users/torvalds/repos?per_page=5"
ALL_REPOS="[]"

while [ -n "$NEXT_URL" ]; do
  # Capture full response (headers + body) to a temp file
  TMPFILE=$(mktemp)
  curl -sD "$TMPFILE" \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Accept: application/vnd.github+json" \
    "$NEXT_URL" \
  | {
      BODY=$(cat)
      ALL_REPOS=$(echo "$ALL_REPOS" "$BODY" | jq -s '.[0] + .[1]')
      echo "$ALL_REPOS" > /tmp/repos_acc.json
    }

  # Extract next URL from Link header
  NEXT_URL=$(grep -i '^link:' "$TMPFILE" \
    | sed -E 's/.*<([^>]+)>; rel="next".*/\1/;t;d')
  rm -f "$TMPFILE"
done

echo "Total repos: $(jq 'length' /tmp/repos_acc.json)"

串联请求——将一次调用的输出用作另一次调用的输入

一种常见的工作流程是:获取列表,提取 ID,然后获取每个 ID 对应的详细信息。使用 $() 命令替换保存中间值,并将这些值传入下一个 URL。

  • 使用 jq -r '.field' 提取单个值。
  • 在 while read 循环中使用 jq -r '.[].id' 遍历多个 ID。
  • 在请求之间使用 sleep,以遵守速率限制。
#!/usr/bin/env bash
# Step 1: get all user IDs from the /users endpoint
# Step 2: for each user, fetch their posts and count them

curl -s 'https://jsonplaceholder.typicode.com/users' \
| jq -r '.[].id' \
| while read -r USER_ID; do
    POST_COUNT=$(curl -s "https://jsonplaceholder.typicode.com/posts?userId=${USER_ID}" \
                 | jq 'length')
    echo "User ${USER_ID}: ${POST_COUNT} posts"
    sleep 0.1   # be polite to the API
done

POST 请求——发送 JSON 载荷

要创建或更新资源,请使用 JSON 请求体发送 POST 或 PUT。使用 -X POST、-H 'Content-Type: application/json' 和 -d 指定请求体。使用 jq -n 构建载荷,避免引号处理问题。

  • jq -n --arg key value '{key: $key}' — 在 jq 中安全地插入变量。
  • 将构造好的 JSON 直接通过管道传入 curl 的 -d @-(从标准输入读取请求体)。
  • 使用另一个 jq 过滤器立即解析响应。
#!/usr/bin/env bash
TITLE="My New Post"
BODY_TEXT="Written via curl and jq."
USER_ID=1

# Build the JSON payload safely and POST it
RESPONSE=$(jq -n \
  --arg title   "$TITLE" \
  --arg body    "$BODY_TEXT" \
  --argjson userId "$USER_ID" \
  '{title: $title, body: $body, userId: $userId}' \
| curl -s \
    -X POST \
    -H 'Content-Type: application/json' \
    -d @- \
    'https://jsonplaceholder.typicode.com/posts')

echo "Created post ID: $(echo "$RESPONSE" | jq '.id')"
echo "Full response:"
echo "$RESPONSE" | jq '.'

错误处理——HTTP 状态码和接口错误

HTTP 连接成功并不意味着接口调用成功。请分别检查 HTTP 状态码和 JSON 错误字段。

  • curl -w '%{http_code}' 会将状态码追加到标准输出;使用 -o 将请求体写入文件。
  • 在脚本中比较状态码,并以不同方式处理 4xx/5xx 状态。
  • 许多接口会在请求体中嵌入 {"error": "..."} — 使用 jq 的 has() 或 type 检查它。
#!/usr/bin/env bash
API_URL='https://jsonplaceholder.typicode.com/todos/99999'
TMPBODY=$(mktemp)

HTTP_CODE=$(curl -s -o "$TMPBODY" -w '%{http_code}' "$API_URL")

if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
  echo "Success ($HTTP_CODE):"
  jq '.' "$TMPBODY"
elif [ "$HTTP_CODE" -eq 404 ]; then
  echo "Resource not found (404). Body:"
  jq '.' "$TMPBODY"
elif [ "$HTTP_CODE" -ge 500 ]; then
  echo "Server error ($HTTP_CODE). Retrying later."
else
  echo "Unexpected status: $HTTP_CODE"
  cat "$TMPBODY"
fi

rm -f "$TMPBODY"

构建可复用的接口辅助函数

将 curl 和错误检查的样板代码封装到 Shell 函数中。该函数负责身份验证、状态检查和 JSON 提取——调用方只需传入端点和 jq 过滤器。

  • 发生 HTTP 错误时返回非零退出码,以便调用方使用 || 或 set -e。
  • 接受 jq 过滤器参数,使同一个函数能够服务于多个端点。
  • 在任何需要使用该接口的脚本中加载此函数文件。
#!/usr/bin/env bash
# api_get <endpoint_path> <jq_filter>
# Returns filtered JSON or exits non-zero on error.
api_get() {
  local PATH_PART="$1"
  local JQ_FILTER="${2:-.}"
  local BASE_URL='https://jsonplaceholder.typicode.com'
  local TMPBODY
  TMPBODY=$(mktemp)

  local HTTP_CODE
  HTTP_CODE=$(curl -s \
    -o "$TMPBODY" \
    -w '%{http_code}' \
    "${BASE_URL}${PATH_PART}")

  if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
    echo "[ERROR] HTTP $HTTP_CODE for ${PATH_PART}" >&2
    rm -f "$TMPBODY"
    return 1
  fi

  jq -r "$JQ_FILTER" "$TMPBODY"
  rm -f "$TMPBODY"
}

# Usage examples
api_get '/todos/1' '.title'
api_get '/posts?userId=1' '[.[] | .title]'
api_get '/users' '.[] | "\(.id) \(.name) <\(.email)>"'

知识检查:分页策略

使用 curl 和 jq,在 Bash 脚本中处理分页 REST 接口响应,检验您对相关方法的理解。

课程回顾:curl + jq 接口脚本编程

现在,您已经拥有一套完整工具,可以从 Bash 命令行调用 REST 接口:

  • 基本流水线:curl -s URL | jq 'filter' — 一切操作的基础。
  • 身份验证标头:通过环境变量使用 -H 传递令牌,绝不要将令牌硬编码。
  • 数组处理:.[]、select() 和 map() 可以筛选和重塑接口响应。
  • 分页:使用页码计数器循环(以空数组作为结束标记),或解析 Link 标头,以适配提供下一页 URL 的接口。
  • 串联请求:从一个响应中提取 ID,并在 while read 循环中将其传入下一个请求。
  • 使用安全载荷发送 POST:使用 jq -n --arg 构建 JSON 请求体,并通过管道传入 curl 的 -d @-。
  • 错误处理:将 HTTP 状态(-w '%{http_code}')与请求体中的接口级错误分开处理。
  • 可复用辅助函数:将样板代码封装到 Shell 函数中,使脚本保持简洁并遵循 DRY 原则。

组合使用这些模式,您就可以完全通过 Shell 自动化任何 JSON 接口工作流程,无需额外的运行时环境。

免费开始

用 AI 导师学习 DevOps Bootcamp — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
142
课程
568

常见问题解答

「结合 curl 与 jq 调用 REST API」课时是免费的吗?

是的 — 「结合 curl 与 jq 调用 REST API」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 DevOps Bootcamp 课程的其余内容,请升级到 CoddyKit PRO。 DevOps Bootcamp 课程共包含 4 节课。

「结合 curl 与 jq 调用 REST API」这节课中我会学到什么?

在脚本中将 curl 请求传入 jq,以提取、分页处理并重新格式化实时 API 响应。 你通过在浏览器中直接运行的动手代码来练习 DevOps Bootcamp,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 DevOps Bootcamp 需要有经验吗?

无需任何先前经验。CoddyKit 上的 DevOps Bootcamp 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「结合 curl 与 jq 调用 REST API」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 DevOps Bootcamp 课中编写并运行代码吗?

能。每节 DevOps Bootcamp 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 jq 管道筛选和选择 JSON
  2. 使用 jq 转换和构建 JSON 对象
  3. 结合 curl 与 jq 调用 REST API
  4. 使用 yq 编辑 YAML 配置文件
← 返回 DevOps Bootcamp