Course 合成输入先经 import 变成 accepted/rejected 两份 JSON Lines。查询只读 accepted:不带条件的 query 输出全部四条;--level WARN 只输出 WARN;--min-duration 0 输出 duration 为数字且大于等于 0 的三条,排除 duration_ms:null;--contains '.*' 只匹配消息里真的含有点星两个字符的 ERROR 记录。查询条件为空、查询结果为空、输入无效,是三个不同状态。

examples/perl-language/bin/logtool 的查询路径先用 read_jsonl($input) 校验所有 JSON Lines,再构造 make_filter(%conditions)。level 只能是 INFO/WARN/ERROR;min-duration 必须是无前导零的非负整数;contains 是字面包含,不当作正则。tools/replay.pl --case all 覆盖 query-all、query-zero、literal-pattern、invalid-level、negative-duration、missing-option 和 missing-file,每个 case 记录 exit、signal、stdout、stderr 和 stdout SHA-256。

过滤不能替代验证。即便查询只关心 WARN,accepted JSON Lines 中任何坏 JSON、错误类型或缺字段都必须让整次查询失败。course-core.t 断言 JSON 数字字符串会被拒绝;tools/replay.pl --case all 的 bad-json 与 missing-file 也都是状态 2。这样“坏记录不符合筛选条件”不会悄悄变成“坏记录不重要”。

04 的真假与比较边界会在这里落到查询参数:--min-duration 0 必须保留数字 0,不能被短路默认值当成“未提供”;--level INFO 用 eq 判断枚举,不用 ==;--contains '.*' 用字面包含,不用裸正则。course-semantics.pl comparison 还用括号固定 defined($duration) && ($duration == 0 || $duration >= 10),防止把 &&、|| 的优先级误读成另一条业务规则。

练习:有 0 条匹配记录的合法文件与无法打开的文件,能否都返回状态 0、STDOUT 空?不能;前者是已确认的空集,后者没有证据表明文件内容满足契约,应非零退出。复跑证据见 completion/evidence/semantics/*replay-all.*;参考 Perl 5.38.2 pod/perlfunc.pod(grep、defined)、pod/perldata.pod(Context)。