#!/usr/bin/env sh # check-link-format.sh — 檢查一個 domain 存取庫的 markdown 連結寫法。 # # 用法: check-link-format.sh # # 檢查一項(掃 {domain-path} 底下全部 *.md,跳過 .git): # 連結一律 [{文字}]({連結})。雙括號那種同 wiki 連結一個都不留。 # # 為什麼要有這支: 雙括號只在目前這個 wiki 內解析。指到別的存取庫時不會報錯, # 畫面上看起來像正常文字或死連結,巡不到也修不了。人工比對每輪都會漏,交給程式判。 # # 判定範圍與其極限: 只看**渲染得出來的正文**。行內程式碼(單引號反引號夾起來的那段) # 與圍籬程式碼區塊都先剝掉再比,理由有二: # 1. 文件解釋「不要用雙括號」時,本來就要把那個寫法原樣寫出來,那是說明不是連結。 # 2. shell 腳本的條件測試語法長得一模一樣,範例貼進 markdown 就會被誤判。 # 代價講白: 有人把真正的連結寫進程式碼區塊,這支抓不到;那種寫法本來也不會被渲染成連結。 # # 輸出: 一行一個不合格項目,格式 {檔案}:{行號}:{命中內容}(stdout);統計摘要走 stderr。 # 結束碼: 0=掃到 markdown 且全部通過 # 1=有不合格項目(清單在 stdout) # 2=用法錯誤(本腳本只吃一個參數) # 3=domain 路徑不存在,或底下一個 *.md 都沒有——**什麼都沒掃**,不等於通過 set -u usage() { echo 'usage: check-link-format.sh ' >&2 exit 2 } [ "$#" -eq 1 ] || usage DOMAIN=${1%/} [ -n "$DOMAIN" ] || usage [ -d "$DOMAIN" ] || { echo "找不到 domain 路徑:$DOMAIN" >&2; exit 3; } TMP=$(mktemp) || { echo "無法建立暫存檔" >&2; exit 3; } trap 'rm -f "$TMP"' EXIT find "$DOMAIN" -name .git -prune -o -type f -name '*.md' -print 2>/dev/null | sort > "$TMP" [ -s "$TMP" ] || { echo "底下沒有 *.md,無文件可掃:$DOMAIN" >&2; exit 3; } total=$(wc -l < "$TMP" | tr -d ' ') hits=$( # shellcheck disable=SC2046 awk ' FNR == 1 { fence = 0 } # 圍籬起訖各自成行,起與訖用同一個判準,所以一個旗標開關就夠。 /^[[:space:]]*(```|~~~)/ { fence = !fence; next } fence { next } { line = $0 gsub(/`[^`]*`/, "", line) if (match(line, /\[\[[^]]*\]\]/)) { printf "%s:%d:%s\n", FILENAME, FNR, substr(line, RSTART, RLENGTH) } } ' $(cat "$TMP") ) if [ -n "$hits" ]; then printf '%s\n' "$hits" echo "連結寫法檢查有不合格項目:共掃 $total 份文件,清單見 stdout" >&2 exit 1 fi echo "連結寫法檢查通過:$total 份文件,連結全部為 [{文字}]({連結})" >&2 exit 0