"""v1.6.7 立条(二十八)谓词载体 —— 【引号形 heredoc 之展开自检】
立条(二十八)(承 ar 裁决 34):
凡报面 / 判据面之值经【命令替换 ∧ 变量展开】者, 须核【其已展开】——
引号形 heredoc(`<<'EOF'`)∧ 单引号字符串【静默抑制展开】⇒ 值为【字面量】;
并须报【展开后之实值】(禁以模板形充值)。
本载体所治之【面】= **仓库内 shell 件之引号形 heredoc 体**:
若作者【本意要展开】而用了引号形 ⇒ 输出为字面 `$AGENT` 之类, 且
**rc=0 ∧ 零告警 ⇒ 与正确输出不可辨**(同族于立条(二十六): 表面正常而实指缺位)。
★ 本载体【不】判「作者本意」—— 那是语义面, 机械不可判。本载体判【登记面】:
凡【引号形 ∧ 体含 `$`-形】者皆入候选; 候选须在【名册】中逐处登记其【为何引号形为正确】
(承立条(二十四): 沿用危险用法须逐处核 ∧ 就地留注)。
未登记之候选 ⇒ 红 —— 强制作者【显式声明】而非默认放行。
★★ 立条(二十八)之【界】(承 ar 裁决 36 §1.3)—— 本载体所判 = 【值面】:
射程 = 【值面】(欲取之实值); 【不含】【对象面】(被测之逐字原文)。
★ 其理: 若机械施用于【对象面】, 则【展开即毁证】—— 证据将不再是【被测之原文】。
⇒ 名册之理由须显名该条属【值面之正确引号形】抑或【对象面之须保持字面量】。
实测例: `verify_release_gate_selftest.sh` 之夹具 heredoc 若内含【被测件之逐字源码行】,
则该 `$` 系【证据之内容】, 非【待展开之值】⇒ 须登记为【对象面】, 禁改。
子命令:
scan 扫 git 追踪之 shell 件, 出候选 ∧ 对名册 ⇒ rc 0/1/2
selftest 阳性对照(夹具十组)⇒ rc 0 = 全符; 1 = 不符; 2 = 阳性对照失效
list 仅列候选(不判名册)—— 供人工勘定
coverage 出【候选面】对表(原始 `<<` 行 vs 已解析 heredoc)—— 查盲区
出口码:
0 = OK(候选全在名册 ∧ 无盲区)
1 = 违规(有未登记候选 / 名册计数不符 / 有盲区 / 有未收束 heredoc)
2 = 阳性对照失效 / 谓词不承证力(禁以 rc=0 读作绿)
面 ∧ 域 ∧ 单位(承立条(二)):
面 = git ls-files 之 shell 件(`*.sh` ∧ `install.sh`)—— 显名, 非工作区全量
域 = 该面内之 heredoc 开启形
单位 = 【heredoc 条】(非行 —— 一条 heredoc 可跨多行)
"""
import os
import re
import subprocess
import sys
SHELL_GLOBS = ["*.sh", "install.sh"]
OPEN_RE = re.compile(r'(?<!<)<<(?!<)(?P<dash>-?)\s*(?P<q>[\'"]?)(?P<tag>[A-Za-z_][A-Za-z0-9_]*)(?P=q)')
DOLLAR_RE = re.compile(r'\$[({A-Za-z_0-9]')
REGISTRY = {
"scripts/dogfood/deploy_km.sh:REMOTE": (
1, "远端 shell 体(`ssh … bash -l -s` 之 stdin)⇒ `$()`/`$VAR` 须【远端】展开; 引号形【正确】(禁本地展开)"),
"scripts/dogfood/startup_km.sh:REMOTE": (
1, "同上 —— 远端 shell 体, 引号形正确"),
"scripts/dogfood/run_dogfood_km.sh:REMOTE": (
3, "同上 —— 远端 shell 体(三处: :52 / :84 / :93), 引号形正确"),
"scripts/dogfood/run_dogfood_km.sh:SH": (
1, "★ 嵌套: 位于 `<<'REMOTE'` 体内(:65)—— 于 km 端写成 `.dogfood-exec.sh` 后由 bash 执行 ⇒ "
"`$(date)`/`${PIPESTATUS[0]}` 须在【执行时】展开; 引号形【正确】"),
"tests/benchmark/swebench/setup.sh:ENVINFO": (
1, "打印给用户【抄入 ~/.bashrc】之文本 ⇒ `$HOME` 须为【字面】; 引号形【正确】"),
"scripts/tests/skip_path_registry_selftest.sh:EOF": (
5, "★ 属【对象面】—— 五条皆【被测之逐字原文】: 夹具(F1/F2/F3/F4/F5 之合成 skip 出口)"
"经 cat > 写入临时档后交 selfaudit_skip_paths() 解析。其 `$roadmap_ver` / `$in_progress_all`"
" 等【须保持字面量】—— 一经本层展开即毁证(被测谓词所见之原文变样, 阳性对照随之失真)"
"⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"scripts/tests/ac8_hack_msrv_leg.sh:PY": (
1, "★ 属【对象面】(v1.7.2 AC8 立)—— 本 heredoc 之体为【判据原文】: 自 `ci.yml` 之 YAML "
"转储中检索【字面量】`cargo +\"$RV\"`(判 `msrv` job 是否以 `+toolchain` 形显式择版)。"
"其 `$RV` 【须保持字面量】—— 一经本层展开即变为 shell 变量之【值】, 检索对象变样 ⇒ "
"该阳性对照失真(G3 之 `plus_rv` 恒 0)⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"scripts/tests/ci_face_leg.sh:REGISTRY": (
1, "★ 属【对象面】(v1.7.2 批10 立 · 承裁决08 裁2 之 `ci-face` 腿)—— 本 heredoc 之体为"
"【名册之逐字原文】(四字段行 `job@@disp@@cmd@@why`), 经 `printf` 写入 `$TMP/reg.txt` 后"
"交 `awk`/`Python` **解析**。其 `$k` / `$RV` / `$cmd` 等【须保持字面量】—— 本 heredoc 之"
"体即【被测之对象】(`G0` 断言其拆解-重组**逐字相符** ∧ `G2` 逐条 `bash -c` 实跑)"
"⇒ 一经本层展开即变为值, 名册变样 ⇒ 二断言随之失真。⇒ 引号形【正确】, "
"承立条(二十八)之界【对象面】。"),
"scripts/tests/no_swallow_leg.sh:ALLOW": (
1, "★ 属【对象面】(v1.9.17 `M1` `P0-3` 立 · 承立条(二十八)之界)—— 本 heredoc 之体为"
"【`J` 类 allow-list 之逐字原文】(行形 `数 @@ 档 @@ 归一形`), 经 `read -r -d ''` 读入 "
"`J_ALLOW` 后由 `awk` **逐字比对**被测面(`verify_release_gate_selftest.sh` / "
"`golden_baseline_sm33.sh` / `install-cargo-binstall.sh` 之吞错形行)。其 `$repo` / `$keys` / "
"`$wrong` / `$bare` / `$BIN_NAME` 等【须保持字面量】—— 键即【被测之对象】(归一形之原文, "
"须与器所扫之实形逐字对表)⇒ 一经本层展开即变为值, 键形变样 ⇒ 匹配随之失真。"
"⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"release/v1.8.3/tools/m2_gate4_watch_recheck_leg.sh:EOF": (
1, "★ 属【对象面】(v1.8.3 `M2` `AC3`-③ 立)—— 本 heredoc 之体为【被测之合成发出面原文】"
"(`:102` `cat > \"$T/ok/emit.sh\" <<'EOF'`), 其后由 `w2_form` 之三形正则**逐字检索** ∧ "
"由 `grep -v '扫描 $scanned 提交'`(`:117`)取【缺扫描形】以造红侧夹具。其 `${head_ref}` / "
"`$window_base` / `$scanned` / `$bad` 【须保持字面量】—— 本 heredoc 之体即【被测之对象】"
"⇒ 一经本层展开即变为值(且该四名于本腿作用域内未定义, `set -u` 下即空/中止), "
"发出面原文变样 ⇒ `W2` 三形断言与「缺扫描形」红侧夹具**同时失真**。"
"⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"release/v1.8.4/tools/ac1_yi_band_backtest_mutations.sh:PY": (
1, "★ 属【对象面】(v1.8.4 `M1` `AC1` 立 · 承立条(二十八)之界)—— 本 heredoc 之体为"
"【变异器之检索/替换模式原文】: Python `MUT`/`MUT5B` 之字符串字面量内嵌【被测腿 "
"`ac1_yi_band_backtest_leg.sh` 之 shell/awk 片段】—— 如 `[ \"$V\" -lt \"$LO\" ] || [ \"$V\" -gt \"$HI\" ]` ∧ "
"awk 域引用 `{v[NR]=$1}` ∧ 消息串 `$KEY=` / `$tag` / `$NPMAX` / `$NNOVAL`, 经 `src.count(old)` "
"**逐字匹配**后写入十二变异副本。其 `$`-名(`V`/`LO`/`HI`/`KEY`/`tag`/`i`/`1`/`NPMAX`/`NNOVAL`)"
"于本器作用域内**皆未定义** ∧ 本器 `set -uo pipefail` ⇒ 去引号即 (a) `-u` 下**即刻中止**"
"(unbound variable), 或 (b) 无 `-u` 时展开为空 ⇒ 检索模式变样 ⇒ `src.count` 得 `0` "
"⇒ `HARNESS_FAIL` ⇒ 十二变异校准**读数失真**(咬合与否不可判)。"
"⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"release/v1.8.4/tools/ac2_predicate_domain_mutations.sh:PY": (
1, "★ 属【对象面】(v1.8.4 `M1` `AC2` 立 · 承立条(二十八)之界)—— 同族: 本 heredoc 之体为"
"【变异器之检索/替换模式原文】—— Python `MUT` 之字符串字面量内嵌被测腿 "
"`ac2_predicate_domain_leg.sh` 之 shell 片段: `[ \"$nface\" -ge 1 ] && [ \"$nclose\" -ge 1 ]` ∧ "
"`judge \"$f\" \"$LIT\"` ∧ `[ \"$OOD\" -gt 0 ]`; `m5` 另须 `MUT5B` 二处同改"
"(复原调用形 ∧ 去显式 `return 0`)。其 `$`-名(`f`/`LIT`/`OOD`/`nface`/`nclose`)于本器"
"作用域内**皆未定义** ∧ 本器 `set -uo pipefail` ⇒ 去引号即 (a) `-u` 下**即刻中止** "
"或 (b) 展开为空 ⇒ 检索模式变样 ⇒ `HARNESS_FAIL` ⇒ 五变异校准**读数失真**。"
"⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"release/v1.8.5/tools/ac1_yi_band_backtest_mutations.sh:PY": (
1, "★ 属【对象面】(v1.8.5 `M3` `AC1` 立 · 承立条(二十八)之界)—— 同族(承 `v1.8.4` 之 "
"`ac1_yi_band_backtest_mutations.sh:PY` 条目): 本 heredoc 之体为【变异器之检索/替换模式原文】—— "
"Python `MUT` 之字符串字面量内嵌【被测腿 `ac1_yi_band_backtest_leg.sh`(`v7`)之 shell/awk 片段】, "
"如 `[ \"$V\" -lt \"$LO\" ] || [ \"$V\" -gt \"$HI\" ]` ∧ awk 域引用 `{v[NR]=$1}` ∧ "
"消息串 `$KEY=` / `$tag` / `$NPMAX` / `$NNOVAL`, 经 `src.count(old)` **逐字匹配**后写入**十三**变异副本"
"(较 `v1.8.4` 增 `m13`: pre-`RUN` 行计数删之 —— 其替换串为 awk 语句 `if (!seen) { npre++; next }`, "
"**不含** `$`-名)。其 `$`-名(`V`/`LO`/`HI`/`KEY`/`tag`/`i`/`1`/`NPMAX`/`NNOVAL`)"
"于本器作用域内**皆未定义** ∧ 本器 `set -uo pipefail` ⇒ 去引号即 (a) `-u` 下**即刻中止**"
"(unbound variable), 或 (b) 无 `-u` 时展开为空 ⇒ 检索模式变样 ⇒ `src.count` 得 `0` "
"⇒ `HARNESS_FAIL` ⇒ 十三变异校准**读数失真**(咬合与否不可判)。"
"⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
"release/v1.9.18/tools/check5_spec_marker_accept.sh:EOS": (
1, "★ 属【对象面】(v1.9.18 `M1-b` 立 · 承立条(二十八)之界)—— 本 heredoc 之体为"
"【被测之判据原文】: `--flip` 翻转对照所造之【合成修面】—— 本器将 `verify_release.sh`"
" 逐字复制后, 于 `in_progress_spec` 之【面内】注入【一行命令】以充当 `dev` 依 `R03` 所补之"
"【节标题臂】, 再由 `awk` 注入该行 ∧ 跑本器於合成面 ⇒ 证【本器能翻转】(判据非恒红)。"
"其 `$(grep -P …)` 与 `$in_progress_spec` 【须保持字面量】—— ★ 本 heredoc 之体即【被测之"
"对象】(所注入者必须是【命令】而非【本器之值】)⇒ 一经本层展开, 注入者遂不再是命令"
"(变为此刻之字面值)⇒ 合成面与真门之形【不再同体】⇒ 该对照**自毁**(`--flip` 之绿"
"将零证力: 所跑者已非器之原行)。⇒ 引号形【正确】, 承立条(二十八)之界【对象面】。"),
}
def shell_files(root):
"""面 = git 追踪之 shell 件。非 git 面 ⇒ 退化为目录扫描并【显名】。"""
try:
out = subprocess.run(["git", "-C", root, "ls-files", "--"] + SHELL_GLOBS,
capture_output=True, text=True, check=True).stdout
files = [f for f in out.split("\n") if f.strip()]
return files, "git ls-files"
except Exception:
files = []
for dp, _dn, fn in os.walk(root):
if "/target/" in dp or "/.git" in dp:
continue
for f in fn:
if f.endswith(".sh") or f == "install.sh":
files.append(os.path.relpath(os.path.join(dp, f), root))
return sorted(files), "os.walk(非 git 面 —— 读数含未追踪件, 慎读)"
def scan_text(text, path):
"""⇒ (候选表, 未收束表, 已解析 heredoc 数, 原始 `<<` 候选行数)
候选 = 引号形 ∧ 体含 `$`-形。
"""
lines = text.split("\n")
cands, unterm = [], []
parsed = 0
raw_ll = 0
consumed = set()
for i, ln in enumerate(lines):
if OPEN_RE.search(ln):
raw_ll += 1
for m in OPEN_RE.finditer(ln):
q, tag = m.group("q"), m.group("tag")
j = i + 1
body = []
while j < len(lines):
probe = lines[j].lstrip("\t") if m.group("dash") else lines[j]
if probe.strip() == tag:
break
body.append(lines[j])
j += 1
if j >= len(lines):
unterm.append((path, i + 1, tag))
continue
parsed += 1
consumed.add(i)
if q in ("'", '"'):
b = "\n".join(body)
if DOLLAR_RE.search(b):
hits = DOLLAR_RE.findall(b)
cands.append({
"file": path, "line": i + 1, "tag": tag,
"quote": q, "nlines": len(body),
"ncmd": len(re.findall(r'\$\(', b)),
"nbrace": len(re.findall(r'\$\{', b)),
"nbare": len(re.findall(r'\$[A-Za-z_0-9]', b)),
"samples": sorted(set(hits))[:6],
})
return cands, unterm, parsed, raw_ll, len(consumed)
def do_scan(root, verbose=True):
files, how = shell_files(root)
all_c = []
all_u = []
tot_parsed = 0
tot_raw = 0
tot_consumed = 0
for f in files:
p = os.path.join(root, f)
try:
text = open(p, encoding="utf-8", errors="replace").read()
except Exception:
continue
c, u, parsed, raw, consumed = scan_text(text, f)
all_c += c
all_u += u
tot_parsed += parsed
tot_raw += raw
tot_consumed += consumed
if verbose:
print(f"面 : {how} 之 shell 件 = {len(files)} 个(谓词: {SHELL_GLOBS})")
print(f"覆盖面 : 原始 `<<`(排除 `<<<`)候选行 = {tot_raw} ∧ 已解析 heredoc 条 = {tot_parsed}")
print(f"单位 : 【条】—— 候选/名册皆以【heredoc 条】计(非行)")
print(f"候选(引号形 ∧ 体含 `$`-形)= {len(all_c)} 条")
blind = tot_raw - tot_consumed
rc = 0
if blind != 0:
rc = 1
if verbose:
print(f"★★ 盲区: {blind} 行含 `<<` 候选而【未被解析】⇒ 上列候选数【不可读作全量】")
from collections import Counter
cnt = Counter(f"{c['file']}:{c['tag']}" for c in all_c)
unreg = []
for key, n in sorted(cnt.items()):
if key not in REGISTRY:
unreg.append((key, n, "未登记"))
elif REGISTRY[key][0] != n:
unreg.append((key, n, f"名册计 {REGISTRY[key][0]} 条 ≠ 实见 {n} 条"))
if verbose:
print()
print(f"{'候选 file:TAG':<52} {'条':>3} {'行':>5} {'$(':>4} {'${':>4} {'$var':>5} 判")
for key, n in sorted(cnt.items()):
rs = [c for c in all_c if f"{c['file']}:{c['tag']}" == key]
verdict = "登记 ✓" if (key in REGISTRY and REGISTRY[key][0] == n) else "★ 未登记/计数不符"
print(f"{key:<52} {n:>3} {rs[0]['line']:>5} {sum(r['ncmd'] for r in rs):>4} "
f"{sum(r['nbrace'] for r in rs):>4} {sum(r['nbare'] for r in rs):>5} {verdict}")
if not cnt:
print(" (无候选)")
if unreg:
rc = 1
if verbose:
print()
for key, n, why in unreg:
print(f"★ 未登记候选: {key}(实见 {n} 条)—— {why}")
print("★ 承立条(二十四): 沿用危险用法须【逐处核 ∧ 就地留注】——")
print(" 请【逐处】核其引号形是否本意(若本意要展开 ⇒ 去引号; 若须字面 ⇒ 入名册并书理由)。")
print(" ★ 承立条(二十八)之【界】: 名册理由须显名【值面】抑或【对象面】——")
print(" 属【对象面】(被测之逐字原文)者【须保持字面量】, 展开即毁证, 禁改。")
if all_u:
rc = 1
if verbose:
for f, ln, tag in all_u:
print(f"★ 未收束 heredoc: {f}:{ln} tag={tag} ⇒ 体之范围不可判")
if verbose:
print()
print(f"判: {'OK ✓' if rc == 0 else '★ 违规(rc=1)'}")
return rc, all_c, tot_raw, tot_parsed, tot_consumed
FIXTURES = [
("夹具一 引号形 ∧ $() ⇒ 须命中", "cat <<'EOF'\nx=$(date)\nEOF\n", True),
("夹具二 非引号形 ∧ $() ⇒ 不命中(正确展开)", "cat <<EOF\nx=$(date)\nEOF\n", False),
("夹具三 引号形 ∧ 纯文本 ⇒ 不命中", "cat <<'EOF'\nhello world\nEOF\n", False),
("夹具四 引号形 ∧ ${} ⇒ 须命中", "cat <<'EOF'\nx=${VAR}\nEOF\n", True),
("夹具五 引号形 ∧ $var ⇒ 须命中", "cat <<'EOF'\nrole: $AGENT\nEOF\n", True),
("夹具六 herestring <<< ⇒ 不误报", "cat <<<\"$x\"\n", False),
("夹具七 缩进形 <<-'EOF' ∧ $() ⇒ 须命中", "cat <<-'EOF'\n\tx=$(date)\n\tEOF\n", True),
("夹具八 双引号形 <<\"EOF\" ∧ $var ⇒ 须命中", "cat <<\"EOF\"\nrole: $AGENT\nEOF\n", True),
("夹具九 体含反斜杠转义 \\$ ⇒ 仍命中(面 = 字面 $)", "cat <<'EOF'\ncost: \\$5\nEOF\n", True),
("夹具十 同档二条 heredoc ⇒ 须计 2 条", "cat <<'EOF'\n$a\nEOF\ncat <<'EOF'\n$b\nEOF\n", True),
]
def do_selftest():
bad = 0
for name, text, want in FIXTURES:
c, u, parsed, raw, consumed = scan_text(text, "<fixture>")
got = len(c) > 0
if name.startswith("夹具十"):
got = (len(c) == 2)
ok = (got == want) if not name.startswith("夹具十") else (got is True)
print(f" {'✓' if ok else '★ 不符'} {name} ⇒ 候选 {len(c)} 条 / parsed {parsed}")
if not ok:
bad += 1
c, u, parsed, raw, consumed = scan_text("cat <<'EOF'\nno terminator\n", "<fixture>")
ok = len(u) == 1
print(f" {'✓' if ok else '★ 不符'} 夹具十一 未收束 heredoc ⇒ 须报 ⇒ unterm {len(u)}")
if not ok:
bad += 1
c, u, parsed, raw, consumed = scan_text("cat <<<\"$x\"\n", "<fixture>")
ok = (raw == 0 and parsed == 0)
print(f" {'✓' if ok else '★ 不符'} 夹具十二 herestring 不计入原始候选 ⇒ raw {raw}")
if not ok:
bad += 1
c, u, parsed, raw, consumed = scan_text("cat <<< word\n", "<fixture>")
ok = (raw == 0 and parsed == 0 and len(u) == 0)
print(f" {'✓' if ok else '★ 不符'} 夹具十三 herestring 裸词 <<< word ⇒ 不误报 ⇒ raw {raw} unterm {len(u)}")
if not ok:
bad += 1
c, u, parsed, raw, consumed = scan_text("# <<< CHECK14_REGISTRY_END\n", "<fixture>")
ok = (raw == 0 and parsed == 0 and len(u) == 0)
print(f" {'✓' if ok else '★ 不符'} 夹具十四 注释内 `# <<< TAG` ⇒ 不误报 ⇒ raw {raw} unterm {len(u)}")
if not ok:
bad += 1
print()
if bad == 0:
print("阳性对照十四组全符 ✓(v1.7.1 增 夹具十三/十四: herestring 裸词 ∧ 注释内 `<<<`)")
return 0
print(f"★★ 阳性对照【失效】: {bad} 组不符 ⇒ 谓词不承证力")
return 2
def do_coverage(root):
files, how = shell_files(root)
print(f"面 = {how}; 件 = {len(files)}")
for f in files:
try:
text = open(os.path.join(root, f), encoding="utf-8", errors="replace").read()
except Exception:
continue
_c, _u, parsed, raw, consumed = scan_text(text, f)
if raw or parsed:
flag = "" if raw == consumed else f" ★ 盲区 {raw - consumed}"
print(f" {f:<56} 原始 << 行 {raw:>2} ∧ 已解析 {parsed:>2} ∧ 消费行 {consumed:>2}{flag}")
return 0
def main():
cmd = sys.argv[1] if len(sys.argv) > 1 else "scan"
root = sys.argv[2] if len(sys.argv) > 2 else os.getcwd()
if cmd == "scan":
rc, *_ = do_scan(root)
return rc
if cmd == "list":
_rc, c, *_ = do_scan(root, verbose=False)
for x in c:
print(f"{x['file']}:{x['line']}\t{x['tag']}\t{x['quote']}\t{x['nlines']}\t{x['samples']}")
return 0
if cmd == "coverage":
return do_coverage(root)
if cmd == "selftest":
return do_selftest()
print(f"未知子命令: {cmd}", file=sys.stderr)
return 2
if __name__ == "__main__":
sys.exit(main())