gogcli `gog gmail unread`:在终端把 Gmail 消息批量标记为未读的完整指南
2026/9/17 16:14:27 网站建设 项目流程

gogcligog gmail unread:在终端把 Gmail 消息批量标记为未读的完整指南

【免费下载链接】gogcliGoogle Workspace in your terminal.项目地址: https://gitcode.com/GitHub_Trending/gogcl/gogcli

在 Google Workspace 终端化工具 gogcli 中,gog gmail unread是 Gmail「Organize」组下的消息状态管理命令,用于把已读消息重新标记为未读(即给消息重新加上UNREAD标签)。它支持按消息 ID 精确操作,也支持用 Gmail 搜索查询语(query)批量处理,并内置--dry-run预览、JSON/TSV 机器可读输出与--max限额保护。读完本文,你可以掌握该命令的完整用法与参数、它在源码层的执行链路(Users.Messages.BatchModify分批调用、标签名到标签 ID 的解析、分页防死循环保护),以及它与gog gmail mark-readgog gmail archive等同类批量操作的异同。

命令概览

gog gmail unread的规范用法如下(mail/emailmark-unread分别为gmailunread的别名,可互换使用):

gog gmail (mail,email) unread (mark-unread) [<messageId> ...] [flags]

命令注册在 internal/cmd/gmail.go 中,帮助文本为 "Mark messages as unread",归属 Organize 分组,mark-unread是其显式别名:

// internal/cmd/gmail.go Unread GmailUnreadCmd `cmd:"" name:"unread" aliases:"mark-unread" group:"Organize" help:"Mark messages as unread"`

它在 gog gmail 父命令下的完整子命令列表中,与mark-read(标记已读)、archive(归档)、trash(移入垃圾箱)并列,构成一组消息组织操作。

核心参数说明

命令本身只有三个直接参数,全部定义在 internal/cmd/gmail_archive.go 的GmailUnreadCmd结构体中:

// GmailUnreadCmd marks messages as unread. type GmailUnreadCmd struct { MessageIDs []string `arg:"" optional:"" name:"messageId" help:"Message IDs to mark as unread"` Query string `name:"query" short:"q" help:"Mark all messages matching this query as unread"` Max int64 `name:"max" aliases:"limit" help:"Max messages (with --query)" default:"100"` }
参数类型默认值说明
[<messageId> ...]位置参数(可多个,可选)要标记为未读的消息 ID;与--query二选一,二者都缺省时命令以用法错误退出
-q,--querystring使用 Gmail 搜索查询语法匹配消息,把全部命中结果标记为未读
--max,--limitint64100使用--query时允许处理的最大消息数(别名--limit

关键行为约束(来自同一文件的gmailBulkLabelOp实现):

  • 必须提供目标:既没有位置消息 ID、也没有--query时,返回用法错误 "provide message IDs or --query";
  • --max必须为正--query模式下--max <= 0会在发起任何 API 请求前就以退出码 2 报 "--max must be > 0"。这一点有测试直接验证(internal/cmd/gmail_archive_test.go 中的TestGmailBulkOps_QueryInvalidMaxFailsBeforeDryRun包含unread zero用例);
  • 消息 ID 会被归一化:位置参数先经normalizeGmailMessageID(internal/cmd/webid.go)处理,因此可以容忍 Gmail Web URL 形式的输入而非裸 ID(归档线程模式的测试用例中就传入了完整的https://mail.google.com/...链接并期望其被解析为 thread ID)。

两种典型用法

按消息 ID 标记未读——把之前读过、想重新处理的邮件恢复成未读状态:

gog gmail unread 18ab12cd34ef5678 gog gmail unread 18ab12cd34ef5678 18ab99cd00ef1122 # 支持一次传多个

用查询批量标记——例如把收件箱里发件人为某人、且本周内的邮件全部恢复未读:

gog gmail unread --query "in:inbox from:alice@example.com after:2026-09-09" gog gmail unread -q "in:inbox older_than:7d is:read" --max 50 -n # 先预览,前 50 条

--query使用的是 Gmail 搜索语法(in:inboxis:unreadfrom:after:等),与gog gmail search使用的查询语法一致。

底层执行链路:从 ID 到UNREAD标签

gog gmail unreadRun方法只有一行核心逻辑,把「加UNREAD标签」委托给共用的批量标签操作函数:

// internal/cmd/gmail_archive.go func (c *GmailUnreadCmd) Run(ctx context.Context, flags *RootFlags) error { return gmailBulkLabelOp(ctx, flags, c.MessageIDs, c.Query, c.Max, []string{"UNREAD"}, nil, "marked as unread", "gmail.unread") }

对比同文件中的姊妹命令可以看到差异全部体现在「增/删哪些标签」上:mark-read移除UNREAD标签,trashTRASH并移除INBOXarchive只移除INBOX,而unread只添加UNREAD、不删除任何标签。dry-run 输出中的操作名gmail.unread也因此在测试 internal/cmd/gmail_archive_test.go 中被断言为added_labels: ["UNREAD"]removed_labels: []

gmailBulkLabelOp的完整执行流程如下:

  1. 归一化与校验:把位置参数归一化为消息 ID,校验「ID 或 query 至少其一」与--max > 0
  2. dry-run 拦截:若带--dry-run(别名--dryrun/--noop/--preview),通过dryRunExit(internal/cmd/dryrun.go)打印包含opmessage_idsquerymaxadded_labelsremoved_labels的结构化请求描述后以退出码 0 结束,不访问网络;
  3. 解析账号并构建 Gmail 客户端requireAccount(flags)依据-a/--account(账号邮箱、别名或auto)确定目标账号,gmailService构建服务实例;
  4. 收集消息 ID--query模式下调用searchMessageIDs分页搜索,结果与位置参数 ID 合并;搜索为空则输出 "No messages found"(JSON 模式下输出{"action":"marked as unread","count":0});
  5. 标签名解析为标签 IDfetchLabelNameToID(internal/cmd/gmail_labels.go)拉取账号标签列表建立名称→ID 映射,再由resolveLabelIDs(internal/cmd/gmail_labels_utils.go)解析。UNREAD是系统标签,该步对其是幂等确认;
  6. 分批调用 BatchModify:按每批最多 1000 个 ID(Gmail API 的users.messages.batchModify上限)构造BatchModifyMessagesRequest{Ids, AddLabelIds: [UNREAD]}循环发送,任一失败会报 "batch modify failed at offset N";
  7. 输出结果:非 JSON 模式打印Marked as unread N messages(单数消息省略 s);JSON 模式输出actioncountaddedLabelsremovedLabels四个字段的对象。

搜索分页的两个细节

--query模式下的searchMessageIDs(internal/cmd/gmail_archive.go)有两个值得注意的工程细节:

  • 每页最多取 500 条:批大小取min(remaining, 500),并只请求messages(id),nextPageToken字段,避免拉取消息正文;
  • 重复分页令牌保护:通过pageTokenGuard检测服务端异常地重复返回相同nextPageToken(会导致死循环),第 2 次即报错 "repeated page token"。internal/cmd/gmail_archive_test.go 中的TestSearchMessageIDsRejectsRepeatedPageTokenTestSearchMessageIDsRejectsRepeatedEmptyPageTokenTestGmailArchiveCmd_QueryRejectsRepeatedPageToken用假 HTTP 服务专门覆盖了这一场景。

机器可读输出与 Agent 安全开关

gog gmail unread继承 gogcli 全部根级标志,以下与脚本/Agent 场景最相关(完整列表见 docs/commands/gog-gmail-unread.md):

标志说明
-n,--dry-run(别名--dryrun/--noop/--preview不修改任何数据,打印将要执行的动作并以成功码退出
-j,--json(别名--machine以 JSON 输出到 stdout,最适合脚本消费
-p,--plain(别名--tsv输出稳定、可解析的 TSV 文本,无颜色
--results-onlyJSON 模式下只输出主结果,去掉nextPageToken等外层字段
--select,--pick(别名--projectJSON 模式下按逗号分隔的字段路径做 best-effort 选择
--wrap-untrusted在 JSON/raw 输出中为抓取到的文本字段包裹外部不可信内容标记,供 Agent 消费时防提示注入
--readonly运行时阻断一切变更类 API 请求;配合auth add还会请求只读 OAuth 作用域
--gmail-no-send拦截 Gmail 发送类操作(Agent 安全开关)
-a,--account(别名--acct指定账号邮箱、别名或auto,多账号场景必用
--client指定 OAuth 客户端名,选择对应的存储凭据与令牌桶
--access-token直接使用给定的 access token(绕过存储的 refresh token,约 1 小时过期)
--quota-project指定计费 Google Cloud 项目(以X-Goog-User-Project头发送,某些 API 在使用--access-token或 ADC 时必需)
--enable-commands/--enable-commands-exact/--disable-commands按点分路径启用/禁用命令前缀,限制 CLI 可用面
--no-input(别名--non-interactive永不交互式提问,出错即失败,适合 CI
--colorauto\|always\|never)、-v/--verbose--home输出着色、详细日志、覆盖配置/数据根目录(等价GOG_HOME

组合示例:在 CI 中安全地批量恢复未读,先预览再执行:

# 预览将影响哪些消息(不联网写操作) gog gmail unread -q "in:inbox from:boss@example.com" --max 100 -n --json # 确认无误后正式执行,仅保留主结果字段 gog gmail unread -q "in:inbox from:boss@example.com" --max 100 --json --results-only

与相关命令的关系

  • gog gmail mark-read:同文件同机制的逆向操作,移除UNREAD标签而非添加;参数结构(messageId...--query--max默认 100)完全一致(docs/commands/gog-gmail-mark-read.md);
  • gog gmail archive/gog gmail trash:同样走gmailBulkLabelOp,区别仅在增删的标签组合;archive额外提供--thread按线程操作的模式(该模式不可与--query同时使用,否则以退出码 2 报 "--thread cannot be used with --query");
  • gog gmail search:只读搜索线程;unread --query是「搜索 + 批量改标签」的写操作版本,二者共享 Gmail 查询语法;
  • 命令树上下文:完整的 Gmail 子命令(get、send、drafts、labels、thread、settings 等)见 docs/commands/gog-gmail.md,全部命令索引见 docs/commands/README.md。

这些文档页均由gog schema --json自动生成(页首注明 "Do not edit this page by hand; runmake docs-commands"),因此文档中的标志定义与源码中的结构体 tag 始终一致。

小结

gog gmail unread的语义模型非常清晰:把「标记未读」实现为「批量给消息添加UNREAD系统标签」,通过Users.Messages.BatchModify按 1000 条一批发送,天然获得幂等性(重复添加同一标签无副作用)与批量效率。配合--query + --max的限额机制、--dry-run预览、JSON/TSV 输出与--readonly等安全开关,它既可以人工交互式使用,也可以安全地嵌入脚本和 Agent 工作流(例如 gogcli 的 Agent 技能gog-inbox-triage就涉及未读邮件的优先级处理,参见 docs/agent-skills.md)。测试文件 internal/cmd/gmail_archive_test.go 对 dry-run 请求内容、--max校验、分页令牌防死循环等均做了断言,为该行为提供了可直接运行的验证依据。

【免费下载链接】gogcliGoogle Workspace in your terminal.项目地址: https://gitcode.com/GitHub_Trending/gogcl/gogcli

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询