Hugohugo list published命令详解:一行命令列出站点全部已发布内容
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
hugo list published是 Hugo 静态站点生成器list命令族中的成员,用于以 CSV 格式输出站点中所有已发布内容的清单——即既不是草稿(draft)、也没有未来发布日期(future)、更未过期(expired)的页面。在内容审核、CI 发布检查、SEO 巡检、与外部 CMS 或脚本对接等场景下,它都能快速给出机器可读的内容全貌。读完本文,你将掌握该命令的过滤规则、CSV 输出格式、全部可用参数,以及它与hugo list drafts / future / expired / all等兄弟命令的差异,并能结合源码理解其"只收集、不渲染"的实现原理。
命令概述:什么是hugo list published
hugo list published是 Hugo 内置的 hugo list 子命令之一,官方对其的定义是:
List content that is not draft, future, or expired. (列出既非草稿、也非未来、更非过期的内容。)
即它的职责非常纯粹:把当前站点中真正"已发布"的页面逐行打印出来。该命令默认不会对任何页面执行 HTML 渲染,因此执行速度很快,非常适合作为数据管道或自动化脚本中的内容审计入口。
快速上手
在站点根目录(即包含hugo.toml/hugo.yaml/hugo.json的位置)执行:
hugo list published输出会以 CSV 表格形式直接打印到标准输出(stdout),例如:
path,slug,title,date,expiryDate,publishDate,draft,permalink,kind,section content/blog/hello.md,hello,Hello Hugo,2024-01-15T10:00:00Z,0001-01-01T00:00:00Z,2024-01-15T10:00:00Z,false,https://example.org/blog/hello/,page,blog首行是固定的表头,之后每一行对应一个已发布页面。CSV 字段使用逗号分隔,字段内部不会出现逗号换行,可直接被 Excel、Pythoncsv模块或awk等工具解析。
过滤逻辑:Hugo 眼中的"已发布"到底是什么
hugo list published之所以与hugo list all不同,关键在于它的包含(include)判定条件。从源码看,commands/list.go 中published子命令的过滤函数为:
shouldInclude := func(p page.Page) bool { return !p.Draft() && !resource.IsFuture(p) && !resource.IsExpired(p) && p.File() != nil }一个页面要进入"已发布"清单,必须同时满足以下四个条件:
| 条件 | 含义 | 判断依据 |
|---|---|---|
!p.Draft() | 页面未被标记为草稿 | 前置元数据中的draft字段 |
!resource.IsFuture(p) | 发布日期未到 | publishDate不在当前时间之后 |
!resource.IsExpired(p) | 未超过过期时间 | expiryDate不在当前时间之前 |
p.File() != nil | 页面来自真实内容文件 | 排除纯内存 / 虚拟页面等无文件来源的页面 |
日期判定的源码级实现
IsFuture与IsExpired定义在 resources/resource/dates.go:
// IsFuture returns whether the argument represents the future. func IsFuture(d Dated) bool { if d.PublishDate().IsZero() { return false } return d.PublishDate().After(htime.Now()) } // IsExpired returns whether the argument is expired. func IsExpired(d Dated) bool { if d.ExpiryDate().IsZero() { return false } return d.ExpiryDate().Before(htime.Now()) }值得注意的两个边界行为:
- 未设置
publishDate(零值时间)时,IsFuture直接返回false,即不会被视为"未来内容"; - 未设置
expiryDate(零值时间)时,IsExpired直接返回false,即永远不会过期; - 判定使用的"当前时间"来自
htime.Now(),而非time.Now()。htime是 Hugo 的可注入时钟,因此你可以通过继承参数--clock人为指定"当前时间",从而模拟未来或过去的时间点来验证发布状态(详见下文参数章节)。
四个前置时间字段
页面是否"已发布",本质上取决于内容前置元数据(front matter)中的四个日期字段。相关字段及别名定义见 docs/content/en/configuration/front-matter.md 与 docs/content/en/content-management/front-matter.md:
| 字段 | 别名 | 对list published的影响 |
|---|---|---|
draft | — | 为true时页面永远不计入已发布 |
publishDate | pubdate,published | 晚于当前时间则视为"未来内容" |
expiryDate | unpublishdate | 早于当前时间则视为"过期内容" |
date | — | 不直接影响过滤,但会出现在 CSV 输出的date列中 |
这也与 Hugo 的常规构建行为保持一致:根据 docs/content/en/getting-started/usage.md,默认情况下 Hugo 不会发布草稿、publishDate在未来的内容,以及expiryDate在过去的内容。也就是说,hugo list published的过滤规则与 Hugo 默认构建时"哪些页面会被发布"的规则是对齐的。
CSV 输出格式详解:每一列代表什么
list家族的 CSV 表头与记录生成逻辑统一定义在 commands/list.go 的createRecord函数与 commands/list.go 的表头写入处:
writer.Write([]string{ "path", "slug", "title", "date", "expiryDate", "publishDate", "draft", "permalink", "kind", "section", })各列含义如下:
| 列名 | 说明 | 取值示例 |
|---|---|---|
path | 内容文件相对于站点工作目录的路径(使用/分隔符) | content/blog/hello.md |
slug | 页面 slug(URL 段) | hello |
title | 页面标题 | Hello Hugo |
date | 页面日期,RFC3339 格式 | 2024-01-15T10:00:00Z |
expiryDate | 过期时间,RFC3339 格式;未设置时为 Go 零值时间 | 0001-01-01T00:00:00Z |
publishDate | 发布日期,RFC3339 格式 | 2024-01-15T10:00:00Z |
draft | 是否为草稿(布尔值) | false |
permalink | 页面最终 URL(由baseURL与路径共同决定) | https://example.org/blog/hello/ |
kind | 页面类型,如page、home、section、taxonomy等 | page |
section | 页面所属的 section(栏目) | blog |
日期统一使用time.RFC3339格式输出;未设置expiryDate或publishDate时输出 Go 时间零值0001-01-01T00:00:00Z(这也再次印证了上方IsFuture/IsExpired中对零值时间的特殊处理)。path列会去掉工作目录前缀并统一为/分隔符,保证跨平台(Windows / Linux / macOS)输出一致。
由于
hugo list published只打印这些结构化字段而不渲染页面,你可以将它的输出重定向到文件,例如hugo list published > published.csv,再交由脚本或 BI 工具做进一步分析。
参数参考:子命令选项与继承选项
hugo list published本身仅有一个选项:
-h, --help help for published与list家族的其他子命令一样,它还会继承来自父命令(hugo根命令)的全部选项:
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory其中与list published关系最密切的是:
--clock:注入一个虚拟的"当前时间"。因为发布 / 过期判定都基于htime.Now(),利用它可以在不修改任何内容文件的前提下,模拟"未来某个时刻"或"过去的某个时刻"来看哪些内容会成为已发布状态。例如:# 把当前时间拨到 2021-11-06,观察届时哪些页面已发布 hugo list published --clock 2021-11-06T22:30:00.00+09:00-s, --source:指定站点根目录。当你在其他目录下执行命令时,用它指向 Hugo 项目位置。-e, --environment:选择构建环境(如development/production),环境不同可能加载不同的配置片段。--config/--configDir:指定配置文件或配置目录,默认依次查找hugo.yaml、hugo.json、hugo.toml(见参数默认值)。--quiet:抑制额外日志,只输出干净的 CSV 数据,适合管道处理。
完整用法示例
# 基本用法:列出全部已发布内容 hugo list published # 从其他目录指向站点并输出到文件 hugo list published -s /path/to/my-site > published.csv # 模拟特定时刻下的已发布清单(用于排期核对) hugo list published --clock 2025-12-31T23:59:59Z与hugo list家族其他子命令的对比
hugo list本身不能单独使用,必须配合子命令(其提示信息为 "List requires a subcommand, e.g. hugo list drafts")。全部五个子命令的过滤逻辑都在 commands/list.go 中定义,对比如下:
| 子命令 | 功能 | 过滤条件(源码) | 备注 |
|---|---|---|---|
hugo list drafts | 列出草稿 | p.Draft() && p.File() != nil | 强制buildDrafts、buildFuture、buildExpired |
hugo list future | 列出未来内容 | IsFuture(p) && p.File() != nil | 强制buildFuture、buildDrafts |
hugo list expired | 列出过期内容 | IsExpired(p) && p.File() != nil | 强制buildExpired、buildDrafts |
hugo list all | 列出全部内容 | p.File() != nil | 含草稿、未来、过期,三者全部强制开启 |
hugo list published | 列出已发布内容 | !p.Draft() && !IsFuture(p) && !IsExpired(p) && p.File() != nil | 不额外开启任何 build 开关 |
可以看到,drafts、future、expired子命令会通过cfg.Set(...)显式打开对应的构建开关(buildDrafts、buildFuture、buildExpired),以便让被过滤掉的页面也能进入 Hugo 的页面集合;而published只调用list(cd, r, shouldInclude),不设置任何开关——因为它要筛选的正是"默认构建开关下会被发布"的那部分页面。
五个子命令共用同一个list函数与同一个 CSV 输出格式,因此列结构完全一致,便于横向比较:例如分别执行hugo list drafts与hugo list published,即可快速盘点草稿积压情况与线上内容清单。
底层实现原理:只收集、不渲染
hugo list published的速度优势来自其构建方式。在 commands/list.go 的list函数中:
bcfg := hugolib.BuildCfg{SkipRender: true}SkipRender: true表示 Hugo 只执行内容收集、页面对象构建与日期逻辑处理,跳过模板渲染与静态文件输出。随后通过h.Pages()(见 hugolib/hugo_sites.go)获取全站页面集合,逐页套用shouldInclude过滤函数,命中者即用csv.Writer写出一行记录。
整体数据流为:
- 读取配置(
flagsToCfg)合并命令行参数; r.Build(...)以SkipRender模式构建站点,产出全站页面集合;- 对每个页面执行
shouldInclude判定(published即"非草稿、非未来、非过期、有文件"); - 命中页面经
createRecord转为 10 列 CSV 记录,写入r.StdOut。
由于不涉及模板执行,该命令在大型站点上也能快速返回结果,适合在 CI 流水线中作为发布前置检查或内容统计工具。
测试验证:官方如何验证该命令的行为
仓库中的测试脚本 testscripts/commands/list.txt 完整覆盖了list家族的行为。它构建了一个包含draft.md(草稿)、expired.md(2019 年过期)、future.md(2090 年发布)、draftfuture.md(草稿 + 未来)、draftexpired.md(草稿 + 过期)的测试站点,并断言:
hugo list drafts只输出draft.md、draftexpired.md、draftfuture.md;hugo list future输出future.md与draftfuture.md,但不输出expired.md;hugo list expired输出expired.md与draftexpired.md,但不输出future.md;hugo list all输出全部五个页面;- 对
hugo list expired传入--clock 2000-01-01T00:00:00Z后,expired.md不再出现在结果中——因为把"当前时间"拨回 2000 年后,expiryDate: 2019-01-01不再早于"当前时间",页面便不再过期。
最后一个用例直观地演示了--clock对日期判定的影响,也再次印证了IsExpired基于htime.Now()的实现。如果你需要在自己的项目中复现,可以参考该测试文件的站点结构,在内容文件的 front matter 中组合设置draft、date、publishDate、expiryDate四个字段。
小结
hugo list published是 Hugo 内容审计工具箱中一个简单而实用的成员:它用"非草稿、非未来、非过期、有文件来源"四个条件精确定义"已发布",以统一的 10 列 CSV 格式输出结构化结果,并借助SkipRender实现轻量快速的内容盘点。结合--clock参数,你还能在任意时间维度上推演站点内容状态,为发布排期、CI 检查和内容治理提供可靠的数据支撑。
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考