Hugo `hugo list published` 命令详解:一行命令列出站点全部已发布内容
2026/9/18 15:50:31 网站建设 项目流程

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页面来自真实内容文件排除纯内存 / 虚拟页面等无文件来源的页面

日期判定的源码级实现

IsFutureIsExpired定义在 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的影响
drafttrue时页面永远不计入已发布
publishDatepubdate,published晚于当前时间则视为"未来内容"
expiryDateunpublishdate早于当前时间则视为"过期内容"
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页面类型,如pagehomesectiontaxonomypage
section页面所属的 section(栏目)blog

日期统一使用time.RFC3339格式输出;未设置expiryDatepublishDate时输出 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.yamlhugo.jsonhugo.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强制buildDraftsbuildFuturebuildExpired
hugo list future列出未来内容IsFuture(p) && p.File() != nil强制buildFuturebuildDrafts
hugo list expired列出过期内容IsExpired(p) && p.File() != nil强制buildExpiredbuildDrafts
hugo list all列出全部内容p.File() != nil含草稿、未来、过期,三者全部强制开启
hugo list published列出已发布内容!p.Draft() && !IsFuture(p) && !IsExpired(p) && p.File() != nil不额外开启任何 build 开关

可以看到,draftsfutureexpired子命令会通过cfg.Set(...)显式打开对应的构建开关(buildDraftsbuildFuturebuildExpired),以便让被过滤掉的页面也能进入 Hugo 的页面集合;而published只调用list(cd, r, shouldInclude),不设置任何开关——因为它要筛选的正是"默认构建开关下会被发布"的那部分页面。

五个子命令共用同一个list函数与同一个 CSV 输出格式,因此列结构完全一致,便于横向比较:例如分别执行hugo list draftshugo 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写出一行记录。

整体数据流为:

  1. 读取配置(flagsToCfg)合并命令行参数;
  2. r.Build(...)SkipRender模式构建站点,产出全站页面集合;
  3. 对每个页面执行shouldInclude判定(published即"非草稿、非未来、非过期、有文件");
  4. 命中页面经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.mddraftexpired.mddraftfuture.md
  • hugo list future输出future.mddraftfuture.md,但不输出expired.md
  • hugo list expired输出expired.mddraftexpired.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 中组合设置draftdatepublishDateexpiryDate四个字段。

小结

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),仅供参考

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

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

立即咨询