如何在 Bash 中格式化 JSON:jq 完整指南
直接在浏览器中使用免费的 JSON格式化工具,无需安装。
在线试用 JSON格式化工具 →当部署脚本开始处理 API 响应或在 CI 中验证配置文件时,掌握如何在 bash 中 格式化 JSON 很快就会变得至关重要。 覆盖 99% 实际使用场景的两个工具是 jq 和 python3 -m json.tool—— 两者都能可靠地处理 bash JSON 格式化管道、通过退出码验证, 并与 CI/CD 工作流无缝集成。若需要一次性检查而不想打开终端, 基于浏览器的 JSON Formatter 可以即时完成。本指南涵盖 jq 安装、管道和文件格式化、验证函数、 GitHub Actions 中的 CI/CD 集成、pre-commit 钩子、heredoc 模式,以及何时使用 Python 标准库作为备选。
- •
jq .同时执行格式化和验证——对无效 JSON 以退出码 1 退出 - • 在 CI 管道中使用
jq -e:输出为空/false/null 时返回非零退出码 - •
jq . file.json > /dev/null && echo "valid"——不改变输出只做验证 - •
python3 -m json.tool无需额外安装,在任何系统上都可使用 - • 永远不要执行
jq . f.json > f.json——shell 会在 jq 读取之前截断源文件
什么是 Bash 中的 JSON 格式化?
bash 中的 JSON 格式化是指将紧凑的、压缩的 JSON 转换为带缩进的、人类可读的输出。 底层数据不变——只有空白字符和换行符不同。在脚本环境中,这有两个重要意义: 调试时的可读性,以及格式化工具作为副作用同时检查语法时的验证性。 像 jq 这样的工具在重新格式化之前会完整地解析 JSON,这意味着一次成功的格式化运行同时也是一次隐式的有效性检查。 这种双重行为——在一步中完成格式化和验证——正是 jq 在自动化管道中如此有用的原因。
{"service":"payments-api","version":"2.4.1","database":{"host":"db-prod-01.internal","port":5432,"pool_size":20},"cache":{"enabled":true,"ttl":300}}{
"service": "payments-api",
"version": "2.4.1",
"database": {
"host": "db-prod-01.internal",
"port": 5432,
"pool_size": 20
},
"cache": {
"enabled": true,
"ttl": 300
}
}jq——在 Bash 中格式化 JSON
jq 是 shell 脚本中 JSON 处理的事实标准(jq 1.6+,bash 4+)。 它是一个专门构建的命令行 JSON 处理器,可以格式化、过滤、转换和验证 JSON。 恒等过滤器 . 将输入原样传递,但经过格式化。 当 jq 无法解析输入时,它以退出码 1 退出——这使其非常适合脚本编写:格式化和验证是单一操作。
安装 jq
# macOS brew install jq # Debian / Ubuntu apt-get install -y jq # Fedora / RHEL / CentOS dnf install jq # Alpine (Docker images) apk add --no-cache jq # Verify jq --version # jq-1.7.1
从 stdin 和文件格式化
# Pipe inline JSON through jq
echo '{"host":"db-prod-01.internal","port":5432}' | jq .
# Format a file directly (prints to stdout)
jq . config/feature-flags.json
# Format with 4-space indentation
jq --indent 4 . config/feature-flags.json
# Format using tabs instead of spaces
jq --tab . config/feature-flags.json将格式化输出写入文件
# Save formatted output (do NOT redirect back to the same file) jq . compact.json > formatted.json # Compact (minify) — reverse of formatting jq -c . formatted.json
jq 对无效 JSON 以退出码 1 退出,成功时为 0,用法错误时为 5。在整个脚本的 if 语句和 || exit 1 保护中使用这些退出码。排序键并去除颜色
# Sort all keys alphabetically (useful for deterministic diffs) jq --sort-keys . config/app-config.json # Disable colour output when writing to a log file jq --monochrome-output . response.json >> deploy.log
jq 选项参考
最常用的 jq 格式化和验证工作流标志:
在 Bash 脚本中验证 JSON
在 jq 中,验证和格式化是同一操作——它在打印之前先解析。当只需要退出码而不需要格式化输出时, 将 stdout 重定向到 /dev/null。 下面的模式可在部署脚本、pre-commit 钩子和 CI 管道中复用。 在事故响应中,我遇到陌生的 API 载荷时做的第一件事就是将其通过管道传给 jq—— 它能将一堵压缩的 JSON 墙变成可读且可调试的内容。
可复用的验证函数
validate_json() {
local file="$1"
if jq . "$file" > /dev/null 2>&1; then
echo "✓ Valid JSON: $file"
return 0
else
echo "✗ Invalid JSON: $file" >&2
return 1
fi
}在无效配置时中止部署
CONFIG="infra/k8s/app-config.json"
validate_json "$CONFIG" || { echo "Aborting deploy: invalid config" >&2; exit 1; }验证目录中的所有 JSON 文件
find ./config -name "*.json" | while read -r f; do jq . "$f" > /dev/null 2>&1 || echo "INVALID: $f" done
-e / --exit-status 标志 更进一步:当输出为 false 或 null 时也会以退出码 1 退出。 用它来断言特定字段为真值: jq -e '.feature_flags.new_checkout' config.json。从文件和 API 响应格式化 JSON
shell 脚本中 JSON 的两个常见来源是磁盘上的文件和通过 curl 获取的 HTTP API 响应。 每种来源的处理模式略有不同。对于文件,主要关注的是安全的原地编辑。 对于 API 响应,关键细节是抑制 curl 的进度条,以防其污染 jq 的输入。
安全的文件原地格式化
# Format and overwrite safely using a temp file tmp=$(mktemp) jq --indent 2 . config/feature-flags.json > "$tmp" && mv "$tmp" config/feature-flags.json echo "Formatted config/feature-flags.json"
格式化 curl API 响应
# Format deployment status from API DEPLOY_ID="dep_8f3a2b9c" curl -s \ -H "Authorization: Bearer $DEPLOY_API_TOKEN" \ "https://api.deployments.internal/v1/deploys/$DEPLOY_ID" \ | jq --indent 2 .
同时格式化和过滤
# Get formatted + filter to errors only from a monitoring endpoint
curl -s "https://monitoring.internal/api/events?level=error&limit=10" \
| jq '[.events[] | {id, message, timestamp, service}]' \
|| { echo "Failed to fetch or parse events" >&2; exit 1; }这里的 || { ... } 模式至关重要。没有它,失败的 curl 或格式错误的 API 响应会静默通过, 脚本中的下一步将对空或不完整的数据进行操作。如果需要在不先编写过滤器表达式的情况下检查复杂的嵌套响应, 基于浏览器的 JSON Formatter 允许您粘贴原始响应并以交互方式浏览树结构。
在 CI/CD 管道中格式化 JSON
CI 是 JSON 验证门控最为重要的地方——到达生产环境的格式错误配置比管道失败要痛苦得多。 大多数文档只介绍 jq 的一次性终端用法;下面的模式是我在生产 SRE 工作流中用于在配置错误到达部署槽之前捕获它们的方法。
GitHub Actions——验证所有 JSON 配置
- name: Validate JSON configs
run: |
echo "Validating JSON configuration files..."
find . -name "*.json" -not -path "*/node_modules/*" | while read -r f; do
if ! jq . "$f" > /dev/null 2>&1; then
echo "::error file=$f::Invalid JSON syntax"
exit 1
fi
done
echo "All JSON files are valid"Pre-commit 钩子——验证暂存的 JSON 文件
#!/usr/bin/env bash
set -euo pipefail
STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.json$' || true)
[ -z "$STAGED" ] && exit 0
for f in $STAGED; do
jq . "$f" > /dev/null 2>&1 || { echo "Invalid JSON: $f"; exit 1; }
done
echo "JSON validation passed"scripts/validate-json.sh, 用 chmod +x scripts/validate-json.sh 使其可执行,然后创建符号链接: ln -s ../../scripts/validate-json.sh .git/hooks/pre-commit。在 Bash 中格式化 JSON 变量和 Heredoc
Shell 脚本经常动态构建 JSON 载荷——来自环境变量、git 元数据或计算值。 最安全的模式是 jq -n --arg / --argjson 而非字符串插值,后者在值包含引号或换行符时会立即失效。 向 jq 管道传递变量时始终用双引号,以防止 JSON 中的空白字符引起单词分割。
格式化存储的 API 响应变量
# Always quote "$API_RESPONSE" — whitespace in JSON would break an unquoted expansion echo "$API_RESPONSE" | jq --indent 2 .
使用 jq -n 构建并格式化载荷
payload=$(jq -n \
--arg env "production" \
--arg version "$(git describe --tags)" \
--argjson replicas 3 \
'{environment: $env, version: $version, replicas: $replicas}')
# Inspect the built payload
echo "$payload" | jq .
# Post it to an API
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DEPLOY_API_TOKEN" \
-d "$payload" \
"https://api.deployments.internal/v1/deploys"--arg 始终绑定字符串值。 --argjson 首先将值解析为 JSON, 因此您可以传递数字、布尔值、数组和对象,而无需在过滤器表达式中对其加引号。在不安装 jq 的情况下在 Bash 中格式化 JSON
当 jq 不可用时——精简的 Docker 镜像、受限的 CI runner,或无法安装软件包的系统—— Python 内置的 json.tool 模块 提供了相同的核心功能。它是 Python 标准库的一部分;只要安装了 Python 3, 无需任何额外依赖即可使用。
# Format from a file python3 -m json.tool config.json # Control indent width python3 -m json.tool --indent 2 config.json # Sort keys alphabetically python3 -m json.tool --sort-keys config.json # Format from stdin (e.g., piped from curl) curl -s https://api.deployments.internal/v1/status | python3 -m json.tool
python3 -m json.tool 比 jq 更严格:它拒绝尾随逗号、注释和 JSON5 扩展。这种严格性对生产配置验证是有益的, 但在处理第三方工具宽松 JSON 时可能带来摩擦。它也不产生彩色输出, 使得终端交互检查不如 jq 方便。# Validation with exit code (same semantics as jq)
python3 -m json.tool config.json > /dev/null && echo "valid" || echo "invalid"
# Inline string validation
echo '{"service":"payments-api","healthy":true}' | python3 -m json.tool > /dev/null
echo "Exit code: $?" # 0 = valid带语法高亮的终端输出
jq 默认会为输出着色——键为蓝色、字符串为绿色、数字为白色。 当您需要在可滚动的分页器中完整查看 JSON 语法高亮, 或在终端会话中调试大型嵌套响应时, bat 提供了最佳体验。 两者都适用于调试和交互检查;在将输出写入文件或 API 响应时都不应使用彩色模式。
安装 bat
# macOS brew install bat # Debian / Ubuntu (binary may be named batcat — alias if so) apt-get install -y bat # alias bat=batcat # add to ~/.bashrc if needed # Verify bat --version # bat 0.24.0
使用语法高亮查看 JSON 文件
# Syntax-highlighted JSON in the pager (press q to exit) bat config/app-config.json # Disable paging — print directly to terminal bat --paging=never config/app-config.json # Pipe jq output through bat for coloured inspection jq '.database' infra/app-config.json | bat --language=json --paging=never
在可滚动分页器中查看彩色 jq 输出
# -C forces color even when stdout is not a tty (e.g. when piping to less) jq -C . logs/deploy-response.json | less -R
bat / jq -C)仅用于 终端检查和调试。写入日志文件或管道到其他工具之前,请去除 ANSI 颜色码——使用 jq -M . (--monochrome-output) 或 bat --plain。在 Bash 中处理大型 JSON 文件
当 JSON 文件超过 50–100 MB 时,使用 jq 的默认模式将其加载到内存可能很慢, 或在内存受限的主机上触发 OOM(例如限制 512 MB 的 Docker 容器)。 jq --stream 在读取时 增量发出路径/值对,无需缓冲整个文档。对于 NDJSON(每行一个 JSON 对象),jq 有更高效的原生方法。
使用 jq --stream 流式处理大型 JSON 文件
# --stream emits [path, scalar] pairs as jq reads the input # Extract all "status" fields from a large log archive without loading it fully jq -c --stream 'if length == 2 and (.[0][-1] == "status") then .[1] else empty end' logs/archive-2026-03.json
NDJSON / JSON Lines——每行处理一个对象
# NDJSON: one JSON object per line — common in Kafka exports, Fluentd, and Logstash
# -R reads raw lines; fromjson? skips lines that are not valid JSON
jq -c -R 'fromjson? | {id: .request_id, status: .http_status, latency: .duration_ms}' logs/access-2026-03-13.ndjson > logs/summary.ndjson# Shell loop alternative — useful when you need per-line error handling
while IFS= read -r line; do
echo "$line" | jq -c '{id: .request_id, status: .http_status}' 2>/dev/null || echo "SKIP: malformed line" >&2
done < logs/access-2026-03-13.ndjsonjq . file.json 切换到 --stream。 对于 NDJSON 管道,优先使用 jq -R 'fromjson?' 而非 shell while read 循环——它显著更快,因为避免了每行生成一个子 shell。常见错误
问题: shell 在 jq 读取输入之前就打开并截断了输出文件。如果源文件和目标文件是同一路径,jq 读取到的是空文件。
修复: 先写入 mktemp 临时文件,再用 mv 原子地替换原文件。
jq --indent 2 . settings.json > settings.json
tmp=$(mktemp) && jq --indent 2 . settings.json > "$tmp" && mv "$tmp" settings.json
问题: 没有错误处理器时,当 JSON 无效时脚本会静默地继续,产生空的或缺失的格式化文件——后续步骤则会因此出现难以理解的错误。
修复: 在每个产生供后续步骤使用的输出的 jq 调用后添加 || { echo '...' >&2; exit 1; }。
jq . response.json > formatted.json
jq . response.json > formatted.json || { echo "Invalid JSON in response.json" >&2; exit 1; }问题: curl 默认将进度条输出到 stderr。当 stderr 与 stdout 合并时(例如在子 shell 或日志捕获中),进度条文本会出现在 jq 的输入中并导致解析错误。
修复: 向 curl 管道到 jq 时始终传递 -s(静默模式)。如需诊断输出,单独使用 -v 或 --fail-with-body。
curl https://api.payments.internal/config | jq .
curl -s https://api.payments.internal/config | jq .
问题: -r / --raw-output 标志会去除顶层字符串值的 JSON 引号——它不会格式化对象或数组。对对象输入传递 -r . 产生的是同样的紧凑对象,而非缩进输出。
修复: 使用 jq .(不带 -r 标志)进行格式化。将 -r 保留用于提取纯字符串值,如 jq -r '.version' config.json。
jq -r . config.json
jq . config.json
jq vs python3 vs json_pp——快速比较
工具的选择取决于您的环境中有什么可用,以及您在基本格式化之外需要什么功能:
对于大多数 bash 脚本和 CI/CD 工作,jq 是正确的默认选择——它在单个二进制文件中验证、格式化、过滤, 并提供可靠的退出码,无需运行时依赖。当无法安装额外软件包且 Python 已存在时, 回退到 python3 -m json.tool。
常见问题
如何在 bash 中原地格式化 JSON 文件?
永远不要将 jq 的输出重定向回同一个文件——shell 会在 jq 读取之前截断该文件。应先写入临时文件,再用 mv 原子地替换原文件。
tmp=$(mktemp) jq --indent 2 . config/app-config.json > "$tmp" && mv "$tmp" config/app-config.json echo "已成功原地格式化"
如何在 bash 脚本中验证 JSON 并在出错时退出?
将文件通过管道或参数传给 jq,并将 stdout 重定向到 /dev/null。使用 || 捕获非零退出码并中止脚本。jq 在任何解析错误时都会以退出码 1 退出,非常适合 CI 门控。
validate_json() {
local file="$1"
if jq . "$file" > /dev/null 2>&1; then
echo "✓ Valid JSON: $file"
return 0
else
echo "✗ Invalid JSON: $file" >&2
return 1
fi
}
validate_json infra/k8s/app-config.json || exit 1如何在不安装 jq 的情况下在 bash 中格式化 JSON?
使用 python3 内置的 json.tool 模块——它随每个标准 Python 安装一起提供,并产生与 jq 相同语义的正确缩进输出和退出码。
# Format from a file python3 -m json.tool config.json # Format from stdin (e.g., a curl response) curl -s https://api.internal/status | python3 -m json.tool --indent 2
如何在 bash 中格式化 curl 的响应 JSON?
始终向 curl 传递 -s(静默模式),以防止进度条污染 jq 的输入。将 curl 的 stdout 直接管道到 jq。
DEPLOY_ID="dep_8f3a2b9c" curl -s \ -H "Authorization: Bearer $DEPLOY_API_TOKEN" \ "https://api.deployments.internal/v1/deploys/$DEPLOY_ID" \ | jq --indent 2 .
如何使用 jq 只格式化 JSON 文件的一部分?
使用 jq 路径表达式代替恒等过滤器(.)来提取并格式化嵌套对象或数组。结果本身就是格式化后的 JSON。
# Format just the database config block
jq --indent 2 '.database' infra/app-config.json
# Format + filter events array to error level only
jq '[.events[] | select(.level == "error") | {id, message, service}]' events.jsonjq 对无效 JSON 返回什么退出码?
jq 对任何解析错误返回退出码 1,当设置了 -e / --exit-status 标志且输出为 false 或 null 时也返回退出码 1。退出码 0 表示成功解析了有效 JSON 并产生了真值输出。退出码 5 表示遇到了用法错误。
# Test exit code directly
echo '{"ok":true}' | jq . > /dev/null 2>&1; echo "exit: $?" # exit: 0
echo '{bad json}' | jq . > /dev/null 2>&1; echo "exit: $?" # exit: 1
# -e flag: exit 1 if output is false/null
echo 'null' | jq -e . > /dev/null 2>&1; echo "exit: $?" # exit: 1相关工具
bash JSON 格式化的浏览器替代品和补充工具——当您需要可视化界面、可分享链接,或在终端外工作时非常有用:
Nadia is a site reliability engineer who lives in the terminal. She writes Bash scripts that process logs, transform data, and orchestrate infrastructure across fleets of servers. She is a heavy user of jq, awk, and sed and writes about shell one-liners, text processing pipelines, data serialisation from the command line, and the practical Bash patterns that SREs reach for when speed matters more than elegance.
Erik is a DevOps engineer who has spent years writing and maintaining the shell scripts that hold CI/CD pipelines together. He writes about Bash best practices, portable POSIX shell, encoding and decoding in shell scripts, secret management from the command line, and the patterns that separate reliable automation scripts from brittle ones. He is a strong believer in making shell scripts readable and testable with tools like bats-core.