[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $wiki = 'D:\Applications\app\kepano-obsidian-main\wiki' $vault = 'D:\Applications\app\kepano-obsidian-main' # Get all available pages in vault (excluding special Obsidian files) $allMd = Get-ChildItem -Path $vault -Filter '*.md' -File -Recurse | Where-Object { $_.FullName -notmatch '\\\.git\\' -and $_.FullName -notmatch '\\node_modules\\' } $availablePages = @{} foreach ($f in $allMd) { $bn = $f.BaseName $availablePages[$bn] = $true } # Add known non-.md references that are valid $validRefs = @( 'Trips.base','Map.base','Places.base','Books.base','Movies.base', '黄鹤楼.base','Templates/Bases/Places.base','Templates/Bases/Map.base' ) foreach ($ref in $validRefs) { $availablePages[$ref] = $true } # Scan wiki files for broken links $brokenLinks = @() $wikiFiles = Get-ChildItem -Path $wiki -Filter '*.md' -File foreach ($f in $wikiFiles) { $content = [System.IO.File]::ReadAllText($f.FullName) $matches = [regex]::Matches($content, '\[\[([^\]|]+)(?:\|[^\]]+)?\]\]') foreach ($m in $matches) { $linkTarget = $m.Groups[1].Value.Trim() # Skip various patterns if ($linkTarget -match '^http' -or $linkTarget -match '^#') { continue } if ($linkTarget -match '^Attachments/') { continue } if ($linkTarget -match '\.base#') { continue } # .base section refs if ($linkTarget -match '^Templates/') { continue } if ($linkTarget -match '^References/') { continue } if ($linkTarget -match '^Categories/') { continue } if ($linkTarget -match '百度百科/') { continue } if ($linkTarget -match '^raw/') { continue } # raw/ references # Check if target exists if (-not $availablePages.ContainsKey($linkTarget)) { $brokenLinks += [PSCustomObject]@{ File = $f.Name BrokenLink = $linkTarget } } } } if ($brokenLinks.Count -eq 0) { Write-Output "BROKEN_LINKS: 0" } else { Write-Output "BROKEN_LINKS: $($brokenLinks.Count)" $brokenLinks | ForEach-Object { Write-Output "$($_.File) -> [[$($_.BrokenLink)]]" } }