Files
ouc_latex_template/build.sh
T
laoyang 8660860495 build.sh 两处输出打磨 #18
1) 页码那行改为原样输出。原先用正则截断到 "page",把结尾的括号吃掉了,
   打出来是 "1 page" 而不是 "(1 page).",看着像少字符。

2) 临时文件改用 ./.build_out.$$ + trap 清理,不再用 mktemp。mktemp 在
   Windows 的 Git Bash 里返回 /tmp/... 形式,随后的 rm 会触发安全检查
   (SAFE_DELETE_INVALID_PATH: embedded drive prefix is not allowed),
   无害但会往输出里插一行噪音。

3) 取回成功后打印的路径转成 Windows 形式(D:/...),不再显示 Git Bash
   的挂载路径(/d/...)。
2026-10-29 12:03:02 +08:00

104 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# 在 WSL 内编译模板,并把生成的 PDF 取回 Windows 侧
#
# 适用场景:开发环境在 WSL 里,编译也在 WSL 里做,只把 PDF 拿回 Windows 看。
# 不追求跨平台通用,是给「Windows + WSL2 + ssh 连入」这套配置用的。
#
# 用法:
# bash build.sh 特殊事项说明表 # 编译该目录下默认主文件
# bash build.sh 试卷 试卷题型说明.tex # 显式指定主文件
# bash build.sh all # 全库所有主文件,逐个编译并取回
#
# 可用环境变量覆盖(都有默认值):
# WSL_HOST 默认 yongquan@127.0.0.1
# WSL_KEY 默认 $HOME/.ssh/wsl_local
# WSL_ROOT WSL 内仓库路径,默认 /home/yongquan/code/ouc_latex_template
# WIN_ROOT Windows 侧仓库路径,默认脚本所在目录
# TEXBIN WSL 内的 latex 命令,默认 xelatex
#
# ⚠️ 两点务必注意
#
# 1) 不要让本脚本的后台运行。WSL2 会按「空闲」把整个虚拟机回收(poweroff),
# 一旦回收,正在编译的 xelatex 连同 systemd 的 PID 1 一起消失,日志也随
# /tmp 清空。本脚本的 ssh 会话在编译期间不断开,正好起到保活作用。
# 若确实需要长时间无人值守编译,得先在 Windows 侧 .wslconfig 里设
# vmIdleTimeout=-1 并 wsl --shutdown 一次让它生效,不能只改文件。
#
# 2) 取回的 PDF 不要提交。同源码编译两次,页面内容一致但内嵌 CreationDate
# 不同,git 会认为文件变了(全库 12 个 PDF 全会变成 M)。提交前用
# git checkout -- '*.pdf' 还原。
set -u
WSL_HOST="${WSL_HOST:-yongquan@127.0.0.1}"
WSL_KEY="${WSL_KEY:-$HOME/.ssh/wsl_local}"
WSL_ROOT="${WSL_ROOT:-/home/yongquan/code/ouc_latex_template}"
WIN_ROOT="${WIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
TEXBIN="${TEXBIN:-xelatex}"
SSH="ssh -i $WSL_KEY -o StrictHostKeyChecking=no $WSL_HOST"
die() { echo "$1" >&2; exit 2; }
[ $# -eq 0 ] && die "用法: bash build.sh <目录名|all> [主文件名]"
DIR="$1"; TEX="${2:-}"
# 全库:先跑批量验证,再逐个取回
if [ "$DIR" = "all" ]; then
$SSH "bash '$WSL_ROOT/../verify_all.sh'"
rc=$?
echo
echo "--- 取回全部 PDF ---"
while IFS= read -r rel; do
[ -z "$rel" ] && continue
if scp -i "$WSL_KEY" -q "$WSL_HOST:$WSL_ROOT/$rel" "$WIN_ROOT/$rel" 2>/dev/null; then
echo " $rel"
else
echo " $rel (取回失败)" >&2
fi
done < <($SSH "cd '$WSL_ROOT' && find . -name '*.pdf' -not -path './.git/*' | sed 's|^\./||'")
exit $rc
fi
# 单目录:目录名与主文件名不一致的,在这里登记
if [ -z "$TEX" ]; then
case "$DIR" in
幻灯片) TEX="slides.tex" ;;
科技报告) TEX="report.tex" ;;
试卷) die "试卷/ 有两个主文件,请显式指定:试卷题型示例.tex 或 参考答案与评分标准示例.tex" ;;
*) TEX="$DIR.tex" ;;
esac
fi
echo "=== WSL 编译: $DIR/$TEX ==="
# 编译输出先落到临时文件,失败时才好把 ! 开头的行捞出来给人看。
# 用当前目录下的隐藏文件名,不用 mktemp —— mktemp 在 Windows 的 Git Bash
# 里返回 /tmp/... 形式,与其后的 rm 组合会触发安全检查(safe-delete),
# 属于无害噪音但会干扰输出。
TMPLOG="./.build_out.$$"
trap 'rm -f "$TMPLOG" 2>/dev/null || :' EXIT INT TERM
$SSH "cd '$WSL_ROOT/$DIR' && $TEXBIN -interaction=nonstopmode -halt-on-error '$TEX'" > "$TMPLOG" 2>&1
rc=$?
LOG="${TEX%.tex}.log"
echo "=== 结论 ==="
# 页码那一行原样输出(含 "pages." 结尾),不要用正则截断 ——
# 截了会把括号吃掉,看起来像 "1 page" 少个字符。
$SSH "cd '$WSL_ROOT/$DIR' && \
printf 'error: %s\n' \"\$(grep -c '^!' '$LOG' 2>/dev/null || :)\"; \
printf 'Overfull: %s\n' \"\$(grep -c 'Overfull' '$LOG' 2>/dev/null || :)\"; \
printf 'Underfull: %s\n' \"\$(grep -c 'Underfull' '$LOG' 2>/dev/null || :)\"; \
grep 'Output written on' '$LOG' 2>/dev/null | tail -1"
if [ "$rc" -ne 0 ]; then
echo "编译失败,日志片段:" >&2
grep -A6 "^!" "$TMPLOG" 2>/dev/null | head -20 | sed 's/^/ /' >&2
exit $rc
fi
PDF="${TEX%.tex}.pdf"
# 打印用户熟悉的 Windows 路径形式,而不是 Git Bash 的 /d/... 挂载路径
WINPDF="$WIN_ROOT/$DIR/$PDF"
if scp -i "$WSL_KEY" -q "$WSL_HOST:$WSL_ROOT/$DIR/$PDF" "$WINPDF"; then
echo "PDF 已取回: $(echo "$WINPDF" | sed 's|^/\([a-zA-Z]\)/|\U\1:/|')"
else
echo "PDF 取回失败" >&2; exit 1
fi