# ------------------------------------------------------- # 从 Markdown 文件提取已有 frontmatter 字段 # 返回: title / date / categories (全局变量) # ------------------------------------------------------- extract_frontmatter() { local file="$1" _TITLE="" _DATE="" _CATEGORIES="" _TAGS="[]"
# 取 --- 之间的内容 local in_front=0 local fm_lines=() while IFS= read -r line; do if [[ "$line" == "---" ]]; then ((in_front++)) || true if (( in_front >= 2 )); then break fi continue fi if (( in_front == 1 )); then fm_lines+=("$line") fi done < "$file"
for line in "${fm_lines[@]}"; do # title if [[ "$line" =~ ^title:\ *\"?(.+?)\"?\ *$ ]]; then _TITLE="${BASH_REMATCH[1]}" # 去掉可能残留的引号 _TITLE="${_TITLE%\"}" _TITLE="${_TITLE#\"}" fi # date if [[ "$line" =~ ^date:\ *(.+)$ ]]; then _DATE="${BASH_REMATCH[1]}" _DATE="${_DATE%"${_DATE##*[![:space:]]}"}" # trim trailing fi # categories (单行列表形式: categories: [a, b]) if [[ "$line" =~ ^categories:\ *\[(.+)\] ]]; then _CATEGORIES="${BASH_REMATCH[1]}" _CATEGORIES="${_CATEGORIES// /}" # 去空格 fi # categories (多行形式,取第一个 - xxx) if [[ "$line" =~ ^-\ (.+)$ ]] && [[ -z "$_CATEGORIES" ]]; then _CATEGORIES="${BASH_REMATCH[1]}" fi # tags if [[ "$line" =~ ^tags:\ *(.+)$ ]]; then _TAGS="${BASH_REMATCH[1]}" fi done }
# ------------------------------------------------------- # 从 git 获取文件的最早提交时间 (ISO 格式) # ------------------------------------------------------- git_commit_date() { local file="$1" local dir dir=$(dirname "$file")
# 找到最近的 .git 目录 local git_dir="" local check="$dir" while [[ "$check" != "/" ]]; do if [[ -d "$check/.git" ]]; then git_dir="$check" break fi check=$(dirname "$check") done
if [[ -n "$git_dir" ]]; then local rel_path rel_path=$(realpath --relative-to="$git_dir" "$file") # 获取最早的提交时间 local ts ts=$(git -C "$git_dir" log --diff-filter=A --follow --format="%aI" -- "$rel_path" 2>/dev/null | tail -1) if [[ -n "$ts" ]]; then # 转成 YYYY-MM-DD HH:MM:SS echo "${ts:0:10} ${ts:11:8}" return fi fi
# fallback: 用文件修改时间 local mtime mtime=$(stat -c %y "$file" 2>/dev/null || stat -f %Sm "$file" 2>/dev/null) echo "${mtime:0:19}" }
# ------------------------------------------------------- # 从第一层目录名生成 category # ------------------------------------------------------- get_category() { local file="$1" local base_dir="$2" local rel_path rel_path=$(realpath --relative-to="$base_dir" "$file") local first_dir="${rel_path%%/*}" # 如果文件直接在 base_dir 下,分类为 default if [[ "$first_dir" == "$rel_path" ]]; then echo "default" else echo "$first_dir" fi }
# ------------------------------------------------------- # 写入 frontmatter 到文件(插入到开头) # ------------------------------------------------------- write_frontmatter() { local file="$1" local title="$2" local date="$3" local categories="$4" local tags="${5:-[]}"
# 构建 frontmatter local fm="---" fm+=$'\n'"title: ${title}" fm+=$'\n'"date: ${date}" if [[ -n "$categories" ]]; then fm+=$'\n'"categories: [${categories}]" fi fm+=$'\n'"tags: ${tags}" fm+=$'\n'"---"$'\n'
# 如果已有 frontmatter,先去掉 if has_frontmatter "$file"; then # 去掉第一个 --- 和第二个 --- 之间的内容(含两行 ---) local tmp tmp=$(awk ' BEGIN { found=0; in_fm=0 } /^---[[:space:]]*$/ { if (found == 0) { found=1; in_fm=1; next } else if (in_fm == 1) { in_fm=0; next } } in_fm == 0 { print } ' "$file") echo "$fm$tmp" > "$file" else # 直接在前面插入 local content content=$(cat "$file") echo "$fm$content" > "$file" fi }
if has_frontmatter "$file"; then # 已有 frontmatter → 提取字段 extract_frontmatter "$file" local title="$_TITLE" local date="$_DATE" local categories="$_CATEGORIES" local tags="$_TAGS"
# 如果提取不到 date,用 git 时间 if [[ -z "$date" ]]; then date=$(git_commit_date "$file") log_warn " frontmatter 中无 date,使用 git commit 时间: $date" fi
# 如果提取不到 title,用文件名 if [[ -z "$title" ]]; then title=$(filename_to_title "$file") log_warn " frontmatter 中无 title,使用文件名: $title" fi
# 如果提取不到 categories,用目录名 if [[ -z "$categories" ]]; then categories=$(get_category "$file" "$base_dir") log_warn " frontmatter 中无 categories,使用目录名: $categories" fi
# ── 5a. 把 public/ 打平到临时目录 ── cd public/ find . -mindepth 1 -type f \( -name "*.md" -o -name "*.markdown" \) | while read -r file; do [[ "$file" == */.* ]] && continue
filename=$(basename "$file")
# 同名文件加分类前缀 if [ -f "$STAGING/$filename" ]; then dir_prefix=$(echo "$file" | sed 's|^\./||' | cut -d'/' -f1) if [ "$dir_prefix" != "$filename" ]; then filename="${dir_prefix}-${filename}" fi fi
cp "$file" "$STAGING/$filename" done cd ..
echo "📁 临时目录文件:" ls "$STAGING/" echo "" echo "📁 _posts 现有文件:" ls "$TARGET/" 2>/dev/null || echo "(空)" echo ""
# ── 5b. 去掉 md 文件的 YAML frontmatter,只留正文 ── strip_frontmatter() { awk ' BEGIN { found=0; in_fm=0 } /^---[[:space:]]*$/ { if (found == 0) { found=1; in_fm=1; next } else if (in_fm == 1) { in_fm=0; next } } in_fm == 0 { print } ' "$1" }
for staging_file in "$STAGING"/*.md "$STAGING"/*.markdown; do [ -f "$staging_file" ] || continue filename=$(basename "$staging_file") target_file="$TARGET/$filename"