脚本通过四个决策驱动 bea:它读取哪个账本、为机器可读输出使用 --json、使用 jq 获取所需值,以及它依赖的退出码。本指南将逐步讲解这四个决策,然后安排它们。
你需要在运行任务的机器上安装 bea,并且有一个它可以访问的账本。如果你正在开始新账本,请先阅读 CLI 快速入门。关于标志、信封键和退出码的所有事实都在 Beancount CLI 参考 中查找,此处不再重复。
显式选择账本
指定文件名。本地命令从 --file、然后是 $BEA_FILE、最后是工作目录中的 ./main.bean 解析其目标,而计划任务很少在你认为的位置运行。
bea --file ~/books/main.bean check
BEA_FILE=~/books/main.bean bea check
cd ~/books && bea check全局选项放在命令之前,如 bea --file main.bean check。如果解析的文件不存在,命令以 2 退出并列出所有三个来源,因此 cron 条目中的拼写错误会大声失败,而不是验证错误的账本。通过 --ledger 标志进行托管目标定位尚不存在;bea 永远不会隐式上传本地文件。
读取 JSON 信封
添加全局的 --json 后,每个受支持的命令都会用相同的信封响应:bea、target、data、truncated 和有界列表上的 limit。金额是十进制字符串,日期是 ISO YYYY-MM-DD,因此该值进行比较是安全的,浮点数永远不会进入管道。信封的键在 JSON 和退出码参考 中列出。
bea --json --file main.bean report income-statement | jq .data.net_profit
bea --json --file main.bean list transaction --limit 2 | jq '.data[0].postings[0].units'
bea --json --file main.bean import statement.csv --csv date=Date,amount=Amount,payee=Payee \
--account Assets:Checking --apply --duplicates skip | jq '.data | {written, ready, duplicates}'这三个命令 — report、list 和 import — 保持其结果形状,因此针对它们编写的 jq 路径仍然有效。bea --json check 和 bea --json query 今天也发出信封,但它们是交给原生 Beancount 可执行文件的命令,因此脚本应依据 check 的退出状态而不是其输出形状。有意识地选择你的重复策略:当导入需要决策时,仍然需要 --duplicates,如 导入演练 所解释的。
在正确的退出码上停止
依据状态分支,并在重试任何写入操作之前读取错误对象。在 --json 模式下,失败时 stdout 上没有任何输出,stderr 上只有一个对象,其 error.category 命名了类别:validation(1)、usage(2)、auth(3)、conflict(4)。
#!/usr/bin/env bash
set -euo pipefail
out=$(mktemp)
err=$(mktemp)
status=0
bea --json --file main.bean report income-statement >"$out" 2>"$err" || status=$?
case "$status" in
0) jq -r '.data.net_profit | to_entries[] | "net profit: \(.value) \(.key)"' "$out" ;;
4) echo "conflict — inspect the ledger before retrying" >&2
jq -r '.error.message' "$err" >&2
exit 4 ;;
*) jq -r '.error | "\(.category) (exit \(.exit_code)): \(.message)"' "$err" >&2
exit "$status" ;;
esac退出 4 是脚本绝不能盲目重试的:它意味着结果是冲突或未知,例如外部编辑在写入过程中到达,或 init 目标已存在。检查账本,然后从新的读取开始重试。退出 1 涵盖验证失败和任何其他运行时错误;error.details 携带各个账本错误,error.result 携带部分写入实际做了什么。非零退出绝不保证没有更改。
无需终端运行
bea 自动停止提示。当 stdin 不是终端、设置了 --json 或 CI 为真(1、true、yes 或 on)时,隐式表示 --no-input。在该模式下,缺少确认会以退出 2 失败,而不是永远等待。
CI=true BEA_NO_UPDATE_NOTIFIER=1 bea --json --file main.bean report balance-sheet
bea --json --file main.bean list transaction --limit 100 --sort oldest读取在终端中是宽松的,在其他地方是严格的。当账本有加载器错误时,query、list 和 report 在 --json 下、管道 stdout 下、CI 为真下或使用 --strict 时退出 1;改用命令自己的 --allow-errors 接受部分答案,这也会在 JSON 中设置 ledger_valid: false 并填充 ledger_errors。--strict 是镜像:即使在终端中它也拒绝部分答案,当人手动运行同一脚本时这正是你想要的。bea check 没有 --allow-errors — 报告错误是它的全部工作 — 并且当它发现任何错误时总是退出 1。设置 BEA_NO_UPDATE_NOTIFIER=1 以静默被动的更新通知;真值的 CI 已经做到了。
安排检查
每晚运行验证,让退出码成为警报。下面的两个块都是模板 — 路径、计划和时间表由你决定。
# crontab -e — 07:15 daily; cron mails you only when bea exits nonzero
15 7 * * * BEA_NO_UPDATE_NOTIFIER=1 /opt/homebrew/bin/bea --file /home/alice/books/main.bean checkname: ledger
on:
schedule:
- cron: "15 7 * * *"
push:
jobs:
check:
runs-on: ubuntu-latest
env:
BEA_NO_UPDATE_NOTIFIER: "1"
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv tool install beancount-io==0.1.0
- run: bea --file main.bean check
- run: bea --json --file main.bean report balance-sheet > balance-sheet.json当任务必须可复现时固定版本,当你更愿意跟踪发布时取消固定。CI 在 GitHub Actions 上已经是真值,因此在你设置任何东西之前提示已关闭,更新通知已静默。这里没有格式化步骤是有意为之。计划任务不应重写它不必重写的文件,因此请在预提交钩子中使用 bea format --check,它不触碰任何文件,并在文件需要格式化时退出 1。
在任务中使用托管凭据
从 CI 提供商的密钥存储中设置 BEA_TOKEN,完全跳过浏览器登录。该令牌从环境中读取,从不写入磁盘,因此不会出现在运行者的主目录中供下一个任务找到。
export BEA_TOKEN="$YOUR_CI_SECRET"
bea --json cloud status退出 0 表示凭据已解析,信封命名了它所属的账户;以 error.category 为 auth 的退出 3 表示未解析,消息区分未设置的凭据和被拒绝的凭据。bea cloud logout 对以这种方式提供的令牌不做任何事 — 它既不会撤销它也不会取消设置它,因为另一个任务可能共享它 — 因此请从仪表板撤销泄露的令牌。本地命令根本不需要凭据;只有 bea cloud 和 bea ask 访问托管服务。完整的变量列表在 设置参考 中。
并非所有内容都以 JSON 回答。bea ask 完全拒绝 JSON 模式,bea cloud login 需要人工,而成功的 bea cloud logout 或 bea cloud ledger clone 不返回 JSON 成功对象 — 请改读它们的退出状态。帮助、版本和 shell 补全输出保持文本形式。
后续步骤
- 使用 CLI 导入演练 自动化银行文件。
- 在 Beancount CLI 参考 中查找任何标志、信封键或退出码。
- 仅在 CLI 用尽时才使用 Python:请参阅 可脚本化工作流。