我在使用obsidian这个笔记工具插件已经安装到103个了,使用效率一般般,偶尔记录个日记主要是几类灵感,打开个obsidian也不是很方便,这两天想使用obsdian的cil命令行,但它还是需要主程序obsidian打开的情况下,同样也不够方便,所以CLI命令更适合给AI调用库笔记使用,可以减少大模型的token的消耗。
于是昨天让AI帮我调试一个脚本,用了一白天一晚上,做了一个powershell的ps1脚本,就是让listary使用命令加参数的方式向obsidian里添加笔记,再也不用因为一个小事情要记录,要打开103个插件的obsdian了。效果不错,放上来分享。
1、listary使用命令
obs 第一条内容-第二条内内容 --第三条内容--第四条内容【】第五条内容[]第六条内容#标签1 #标签2。内容里可以加常规的标点符号。只要不加-,--,【】,[],#,这几个符号就行。目前我还没有需要,如果有需要可以调试,你也可以用这个脚本继续调试。
2、命令解释和效果:
普通内容,第一条是在命令提示关键词obs后加空格加内容就可以,后边的加“-”一个字符普是新加加一条普通内容;“--”两个字符就是添加一条无序列表,“【】”和“[]”这两种字符代表添加一条任务列表 “#标签 ”代表#号+文案+空格是添加标签的,并会被添加到笔记的yaml属性区。
命令后的内容可以前后调换使用,没有使用顺序要求,会有个别情况输入非常不规范导致添加的标签可能没有被添加到yaml区或yaml标签区被[xxxx]这种符号嵌套异常。不过一般人不会像我这么抽风容易干出这个事情。![]()
![]()
效果如下:
3、脚本如下:ps:又修改一版
使用:新建一个txt文件,把代码粘贴进去,保存成ob_append.ps1文件,编辑格式为UTF-8 BOM,很重要。
# 🌟 核心接收:直接无脑吸入 Listary 传来的纯文本内容
$userInput = $args -join " "
try {
# 1. 强制 UTF-8 编码写入 (不带BOM,维持文件哈希纯净)
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
# 2. 获取时间与路径变量
$dateObj = Get-Date
$dateStr = $dateObj.ToString("yyyy-MM-dd")
$weekDay = $dateObj.ToString("ddd", [System.Globalization.CultureInfo]::CreateSpecificCulture("zh-CN"))
$dayOfWeek = [int]$dateObj.DayOfWeek
if ($dayOfWeek -eq 0) { $dayOfWeek = 7 }
$weekNum = [math]::Floor(($dateObj.DayOfYear - $dayOfWeek + 10) / 7)
$fileName = "日记-$dateStr-$weekDay-$weekNum.md"
# ⚠️⚠️⚠️ 请确认这 3 个路径在你的电脑上是真实的!⚠️⚠️⚠️
$path = "D:\用户目录\Documents\xuzm-obsidian\日记\$fileName"
$templatePath = "D:\用户目录\Documents\xuzm-obsidian\资源\模板\T_日记模板.md"
$errorLogPath = "D:\用户目录\Documents\xuzm-obsidian\日记\error_log.txt"
$h = $dateObj.ToString("HH")
$m = $dateObj.ToString("mm")
$dateTimeStr = $dateObj.ToString("yyyy-MM-dd HH:mm")
# 3. 清洗输入内容 (脱去 Listary 传来的外衣双引号)
$text = $userInput -replace '^"|"$', ''
$text = $text -replace '\{\{date\}\}\s*', ''
$text = $text.Trim()
# 4. 提取并剥离标签
$foundTags = @()
$extractRegex = '#([a-zA-Z0-9_\u4e00-\u9fa5]+(?:/[a-zA-Z0-9_\u4e00-\u9fa5]+)?)(?=\s|$)'
foreach ($match in [regex]::Matches($text, $extractRegex)) {
$foundTags += $match.Groups[1].Value
}
if ($foundTags.Count -gt 0) {
$removeRegex = '#[a-zA-Z0-9_\u4e00-\u9fa5]+(?:/[a-zA-Z0-9_\u4e00-\u9fa5]+)?(?=\s|$)\s*'
$text = ($text -replace $removeRegex, '').Trim()
$text = $text -replace ' {2,}', ' '
}
# 🌟 5. Thino 专属安全切割引擎 🌟
$text = $text -replace '\\n', '<<BREAK>>'
$text = $text -replace "`r`n|`n", '<<BREAK>>'
# 将触发符统一转为底层标记
$text = $text -replace '--', '<<LIST>>'
$text = $text -replace '-', '<<BREAK>>'
$text = $text -replace '[\[【][\]】]', '<<TASK>>'
# 清洗多余占位符
$text = $text -replace '\s*<<BREAK>>\s*', '<<BREAK>>'
$text = $text -replace '\s*<<LIST>>\s*', '<<LIST>>'
$text = $text -replace '\s*<<TASK>>\s*', '<<TASK>>'
# 开头无标记则默认是第一句普通文本
if ($text -notmatch '^(<<BREAK>>|<<LIST>>|<<TASK>>)') {
$text = "<<BREAK>>" + $text
}
# 切成数组并分配前缀
$itemsRaw = $text -split '(?=<<BREAK>>|<<LIST>>|<<TASK>>)'
$formattedLines = @()
$isSingleBreak = $true # 是否只有一条纯文本记录
foreach ($item in $itemsRaw) {
$item = $item.Trim()
if ($item -eq '') { continue }
if ($item -match '^<<BREAK>>') {
$val = $item -replace '^<<BREAK>>', ''
if ($val -ne '') { $formattedLines += $val }
} elseif ($item -match '^<<LIST>>') {
$val = $item -replace '^<<LIST>>', ''
if ($val -ne '') { $formattedLines += "- $val" }
$isSingleBreak = $false
} elseif ($item -match '^<<TASK>>') {
$val = $item -replace '^<<TASK>>', ''
if ($val -ne '') { $formattedLines += "- [ ] $val" }
$isSingleBreak = $false
}
}
$NL = "`n"
$TAB = "`t"
$finalBlock = ""
# 🌟 6. 判断排版组装内容块 (完美适配 Thino 4大场景) 🌟
if ($formattedLines.Count -gt 0) {
if ($formattedLines.Count -eq 1 -and $isSingleBreak) {
# 场景 1:单条普通记录 -> 直接同行显示
$finalBlock = "- ${h}:${m} $($formattedLines[0])"
} else {
# 场景 2,3,4:多条/列表/任务/混排 -> 换行缩进显示
$finalBlock = "- ${h}:${m} " + $NL
foreach ($line in $formattedLines) {
$finalBlock += $TAB + $line + $NL
}
$finalBlock = $finalBlock -replace '\s+$', '' # 清理尾部多余空行
}
}
$finalContent = ""
# ---- 7. 写入逻辑分支 ----
if (Test-Path $path) {
# 追加模式:保持无缝紧凑排版
$content = Get-Content $path -Raw -Encoding UTF8
if ($finalBlock -ne "") {
$content = $content -replace '\s+$', ''
if ($content -ne "") {
$content += $NL + $finalBlock
} else {
$content += $finalBlock
}
}
$finalContent = $content
} else {
# 新建模式:第一条记录前面留出一个空行
$initContent = ''
if (Test-Path $templatePath) {
$initContent = Get-Content $templatePath -Raw -Encoding UTF8
$initContent = $initContent -replace '<%\s*tp\.file\.creation_date\(\)\s*%>', $dateTimeStr
$initContent = $initContent -replace '<%\s*tp\.file\.last_modified_date\(\)\s*%>', $dateTimeStr
# 双引擎天气获取
try {
$weatherStr = ""
if (Get-Command "curl.exe" -ErrorAction SilentlyContinue) {
$oldEnc = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$weatherUrl = "https://wttr.in/%E5%9C%9F%E9%BB%98%E7%89%B9%E5%B7%A6%E6%97%97?lang=zh&format=%l:+%c+%C+|+%t+|+体感温度+%f+|+湿度+%h+|+风速+%w+|+月相+%m"
$weatherStr = (& curl.exe -s -m 6 -A "curl" -H "Accept-Language: zh-CN,zh;q=0.9" $weatherUrl 2>$null).Trim()
[Console]::OutputEncoding = $oldEnc
}
if ([string]::IsNullOrWhiteSpace($weatherStr) -or $weatherStr -match "500 Internal" -or $weatherStr -match "ERROR") {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$safeUrl = "https://wttr.in/%E5%9C%9F%E9%BB%98%E7%89%B9%E5%B7%A6%E6%97%97?format=3"
$req = [System.Net.WebRequest]::Create($safeUrl)
$req.Timeout = 5000
$req.UserAgent = "curl"
$req.Headers.Add("Accept-Language", "zh-CN,zh;q=0.9")
$res = $req.GetResponse()
$reader = New-Object System.IO.StreamReader($res.GetResponseStream(), [System.Text.Encoding]::UTF8)
$weatherStr = $reader.ReadToEnd().Trim()
$reader.Close(); $res.Close()
}
} catch {
$weatherStr = "获取天气失败"
}
$initContent = $initContent -replace '(?s)<%\*.*?wttr\.in.*?%>', $weatherStr
}
if ($finalBlock -ne "") {
$initContent = $initContent -replace '\s+$', ''
if ($initContent -ne "") {
# 🚀 修改点:新建笔记时,强制留出两个换行符(即空出一行)
$initContent += $NL + $NL + $finalBlock
} else {
$initContent += $finalBlock
}
}
$finalContent = $initContent
}
# 最后给整个文件末尾补上一个标准的换行符,养成好习惯
$finalContent = ($finalContent -replace '\s+$', '') + $NL
# ---- 8. 属性区 (YAML) 标签智能写入 ----
if ($foundTags.Count -gt 0) {
$tagsToAdd = @()
foreach ($t in $foundTags) {
if ($finalContent -notmatch "(?m)^ - " + [regex]::Escape($t) + "\s*$") {
$tagsToAdd += $t
}
}
if ($tagsToAdd.Count -gt 0) {
$tagStr = ""
foreach ($t in $tagsToAdd) { $tagStr += " - $t$NL" }
if ($finalContent -match "\A---\n") {
if ($finalContent -match "(?m)^tags:\n") {
$finalContent = $finalContent -replace "(?m)^(tags:\n)", "`$1$tagStr"
} else {
$finalContent = $finalContent -replace "\A---\n", "---${NL}tags:${NL}${tagStr}"
}
} else {
$finalContent = "---${NL}tags:${NL}${tagStr}---${NL}" + $finalContent
}
}
}
# 9. 彻底写入文件
[System.IO.File]::WriteAllText($path, $finalContent, $utf8NoBom)
} catch {
# 🌟 遇到任何错误,直接弹窗报错!
try {
Add-Type -AssemblyName System.Windows.Forms
$msg = "脚本发生错误!`n`n错误信息: $($_.Exception.Message)`n出错代码行: $($_.InvocationInfo.ScriptLineNumber)"
[System.Windows.Forms.MessageBox]::Show($msg, "Obsidian 脚本报错", 0, [System.Windows.Forms.MessageBoxIcon]::Error)
$errorMsg = "发生时间: $(Get-Date)`n错误内容: $($_.Exception.Message)`n出错代码行: $($_.InvocationInfo.ScriptLineNumber)`n-----------------`n"
[System.IO.File]::AppendAllText($errorLogPath, $errorMsg, (New-Object System.Text.UTF8Encoding $false))
} catch {}
}
4、脚本解释:
4.1 自动在 “x盘:\aaa\bbb\obsidian库\日记\”路径下建立名为“日记-2026-06-11-周四-24.md” 这样的笔记。原来我只有 “日记-2026-06-11”,只对文件名排序有好处,新的命名可以知道是哪个月周几,一直第几个周了。此路径可以自定义,把脚本里的路径都统一改了
4.2 自动添加yaml创建和修改时间,通过https://wttr.in,自动添加一个天气内容。默认添加一个#日记的标签,其它标签在命令里添加
4.3 兼容obsidian插件Thino显示,在这个插件里,笔记内容要加时间和内容的一个制表符,会显示在thino插件区。这样就是一些笔记的标题格式显示不太好。
这个效果我之前用cmd命令调试,只能obsidian笔记区显示,就这个搞了我一天的时间,晚上我回去让ai用ps1脚本调试,一下子就正常了。
5、脚本调用
5.1 在listay的选项里找到命令点右侧下面的+号新建命令,按图添加内容,关键字可以自定义,标题可以自定义,路径:powershell.exe,参数:-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "D:\用户目录\Documents\笔记库\资源\脚本\ob_append.ps1" "{query}",注意把引号也要加入去,路径就是上面让你新建txt改成ps1文件脚本的路径。可以按我的放在笔记库的资源文件夹里。
5.2 静默执行,可以先不用打勾,看看笔记有问题吗,没有问题再勾上。然后双ctrl键,激活listary启动器,输入第一天的命令试一下,就可以无感记录了。
如图:
6、关于记录时间的说明
6.1 一般的的记录是添加内容带格式,我这为了兼容thino,主要是要在记录时添加时间。而一般的添加时间试,就是什么时候添加内容,就加一个时间,我感觉这样太琐碎了,所以我把时间修改了一下。(不再使用)
让他按小时记录,只要在一个小时内,就按这一个小时内第一条添加的时间收集,不再添加当前时间。超过一个小时的,就是超过这个整点的,又让他在当前时间新创建一个时间标签儿,添加新内容。(不再使用)
原来兼容显示一小时内集中收集有个问题,就是再添加记录时,在插件thino里不显示,得在插件修改保存才可以。所以新的方案不再集中到一小时收集了,只要新添加记录内容就会添加一个时间,这样就都会显示在thino里。(不再使用)
不过有一个小缺陷就是单行的时候,thino会跟在时间后面,此脚本还是会单独列在时间下面。
第三版已经完全兼容thion的显示和排版逻辑了。 ![]()






