v0.9.10: hl7-diff-set (batch route-set compare) + hl7-diff --format text readability (F-8)
- hl7-diff-set: compare a whole set of Windows-vs-Linux regression outputs in one command — takes two prefixes (+ optional --dir), auto-discovers routes by glob, runs hl7-diff per pair (--framing auto --ignore MSH.7, overridable), reports per-route PASS/DIFF/MISSING/EXTRA + summary, exit 0 iff all clean (MISSING and EXTRA both count as regressions). No more hand-written for-loop with hardcoded names. - F-8: hl7-diff --format text now shows SEG.FIELD left='...' right='...' with visible (empty)/<absent> tokens so a dropped component (e.g. Linux MSH.9.3 absent vs Windows ADT_A02) is obvious without opening the message files; --------- message N header now uses the real message index (was a stale counter). tsv/count formats unchanged. Consolidated by Clover; Vera CODE-PASS (Deliverables/2026-07-23-cloverleaf-larry-v0910-hl7-diff-set.md) — own-fixture verification of all route classifications + the absent-token render + F-7 no-hang. Live-acceptance = Bryan's regression run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4a1d2451f3
commit
4828862a8e
57
CHANGELOG.md
57
CHANGELOG.md
@ -4,6 +4,63 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here.
|
|||||||
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
||||||
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
||||||
|
|
||||||
|
## v0.9.10 — 2026-07-23
|
||||||
|
|
||||||
|
New: `hl7-diff-set` (`lib/hl7-diff-set.sh` + `bin/hl7-diff-set`, also runnable
|
||||||
|
as `larry tools hl7-diff-set`) — Bryan-direct ask, the natural finish to
|
||||||
|
today's regression tooling. Compares a WHOLE SET of paired baseline-vs-
|
||||||
|
candidate regression output files in one command instead of a hand-written
|
||||||
|
for-loop with hardcoded route names. Built for `hciroutetest`'s real on-disk
|
||||||
|
layout: one directory holding paired files named `<prefix>.<route>` where two
|
||||||
|
prefixes (e.g. `adt_in_regression_out_windows_single` /
|
||||||
|
`adt_in_regression_out_linux_single`) share a set of route suffixes. Auto-
|
||||||
|
discovers routes by globbing both prefixes (`<prefix>.*`, portable bash-3.2-
|
||||||
|
safe glob, no `nullglob`/bash-4-isms); runs the EXISTING `hl7-diff.sh` engine
|
||||||
|
per pair (not reimplemented) with sane regression defaults (`--framing auto
|
||||||
|
--ignore MSH.7`, both overridable, plus `--include-fields` pass-through);
|
||||||
|
reports PASS/DIFF/MISSING/EXTRA per route (a baseline route with no candidate
|
||||||
|
file, or vice-versa, is reported — never silently skipped, and both count as
|
||||||
|
non-clean) plus a final summary line (`4 routes: 3 clean, 1 differs`). Exit 0
|
||||||
|
iff every route is PASS, so it is a single scriptable regression gate.
|
||||||
|
`--format text` (default) also prints the full `hl7-diff --format text`
|
||||||
|
detail (indented) for any differing route; `--format tsv` prints one
|
||||||
|
machine row per route (`route\tstatus\tcount`) with the summary line routed
|
||||||
|
to stderr so stdout stays clean tsv. Self-tested (4-route mixed fixture: 1
|
||||||
|
clean, 1 differing with a real absent-component diff, 1 dropped-on-candidate
|
||||||
|
MISSING, 1 candidate-only EXTRA) under both `bash --version` 5.3.9 and the
|
||||||
|
real `/bin/bash` 3.2.57 on this Mac — identical output, confirming the
|
||||||
|
bash-3.2/MobaXterm-safety constraint. Bryan's exact command for his live
|
||||||
|
single-site directory:
|
||||||
|
```
|
||||||
|
hl7-diff-set adt_in_regression_out_windows_single adt_in_regression_out_linux_single \
|
||||||
|
--dir /opt/cloverleaf/cis2025.01/integrator/adt/data/regression_testing/single
|
||||||
|
```
|
||||||
|
|
||||||
|
Fix: F-8 (tsk-2026-07-23-hl7diff-readability, Bryan-direct live feedback) —
|
||||||
|
`hl7-diff --format text` printed only the field path plus ONE value per diff
|
||||||
|
line (`MSH.9.3 ADT_A02`); the right-hand value was always present in the
|
||||||
|
underlying data (tsv format carried it correctly) but rendered as invisible
|
||||||
|
trailing whitespace when it was empty, so it looked like only one side was
|
||||||
|
shown. Confirmed against Bryan's live repro: Windows `MSH-9=ADT^A02^ADT_A02`
|
||||||
|
vs Linux `MSH-9=ADT^A02` — `MSH.9.3` is `ADT_A02` on Windows and genuinely
|
||||||
|
ABSENT (not blank) on Linux (only 2 components). Text mode now labels both
|
||||||
|
sides explicitly (`MSH.9.3 left='ADT_A02' right='<absent>'`), and
|
||||||
|
distinguishes an empty-but-present value (`(empty)`) from a component or
|
||||||
|
subcomponent that does not exist AT ALL on that side (`<absent>` — absence
|
||||||
|
cascades: a fully-absent field marks every component/subcomponent under it
|
||||||
|
absent too, regardless of that component's own split count). Also fixed in
|
||||||
|
the same pass: the `----- message N -----` header used a stale running
|
||||||
|
`DIFF_COUNT` guess printed BEFORE processing each message, so a clean
|
||||||
|
message 1 followed by a differing message 2 got the message-2 diffs
|
||||||
|
mislabeled under a "message 1" header (or no header at all) — the header is
|
||||||
|
now printed lazily, inside `emit()`, keyed off the real `msg_idx`, right
|
||||||
|
before that message's first emitted line. tsv/count output formats are
|
||||||
|
unchanged (still raw left/right values, no display tokens). Self-tested:
|
||||||
|
2-message fixture (message 1 clean, message 2 differs) now correctly shows
|
||||||
|
`----- message 2 -----`; separate fixture proves the `(empty)` token for a
|
||||||
|
genuinely-blank-but-present component. Verified `bash -n` clean and
|
||||||
|
`make-manifest.sh --check` clean.
|
||||||
|
|
||||||
## v0.9.9 — 2026-07-23
|
## v0.9.9 — 2026-07-23
|
||||||
|
|
||||||
Fix: REPL leaves the terminal corrupted after exit — Backspace prints a
|
Fix: REPL leaves the terminal corrupted after exit — Backspace prints a
|
||||||
|
|||||||
12
MANIFEST
12
MANIFEST
@ -23,7 +23,7 @@
|
|||||||
# scripts/make-manifest.sh and bump VERSION.
|
# scripts/make-manifest.sh and bump VERSION.
|
||||||
|
|
||||||
# Top-level scripts
|
# Top-level scripts
|
||||||
larry.sh 8da143113410eaf99aeea14ba12ebb13344b568d76d4711984d4c1dd75cbd4e0
|
larry.sh 322316fefec54d88f0a0ac346129874195b6de4786739b2aa43e15a4fa3eb206
|
||||||
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
||||||
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
||||||
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
||||||
@ -31,9 +31,9 @@ install-larry.sh 19f42e8f6e54eaffffe33e1464a8d245721260aed047270a75987c0b25c936c
|
|||||||
uninstall-larry.sh c53ad2d8354c7adeb243b541f027f3f481e4a8661eecfd7af14d7ca53cfcaad9
|
uninstall-larry.sh c53ad2d8354c7adeb243b541f027f3f481e4a8661eecfd7af14d7ca53cfcaad9
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
VERSION 050990d5e9bd0494fa852a4a9ab0d60b91c583a63307920c7887a39e692f9998
|
VERSION cd8f34a6873bdd24cb055e5d97d11fc0dac541a6939c13ef3b700c6bd28344e4
|
||||||
MANUAL.md 46cc8650bbd5daed20617dcf8626e0653da25bf2facf6781912087fcb7c43e57
|
MANUAL.md 7f442b2e3e0bfa7d8cadb588abb3f197dc03bc44f47b84fc5458edba8010ddfa
|
||||||
CHANGELOG.md a14b2be47d8db54419551218df02f4d4f4428e638f5ab7de58426c3aa8c11c2e
|
CHANGELOG.md 4dc1258c1de7ec0d116c881e562140d140d0ca987110c3d2afb060903b4a8cfe
|
||||||
|
|
||||||
# Agent personas (system-prompt overlays)
|
# Agent personas (system-prompt overlays)
|
||||||
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||||
@ -74,7 +74,8 @@ lib/journal.sh 1c285df3f5677477c7b675c4f03236823005670b338a1781dd0c73d12fd1424f
|
|||||||
# HL7 utilities
|
# HL7 utilities
|
||||||
lib/hl7-sanitize.sh efd333dc3e267cf2280433e53bbb910023d0bc4b30724181b53873b410d83005
|
lib/hl7-sanitize.sh efd333dc3e267cf2280433e53bbb910023d0bc4b30724181b53873b410d83005
|
||||||
lib/hl7-desanitize.sh 2e5462a61ab1e8bd3fefb956bace8ca1ae33397a09024cbe766fa55c37a5aad6
|
lib/hl7-desanitize.sh 2e5462a61ab1e8bd3fefb956bace8ca1ae33397a09024cbe766fa55c37a5aad6
|
||||||
lib/hl7-diff.sh 1d81163869d003bb6528818553417350c1ab885f2f04eea5d2a46a06100e789b
|
lib/hl7-diff.sh 79e92c5c9326f6dab49673b719259ae46aaa1020b37b8ac91b5a26869f17656b
|
||||||
|
lib/hl7-diff-set.sh 40d40b1f8e688c584f03bc6efb9645e18daf0fd9081a595b08137d2438619777
|
||||||
lib/hl7-field.sh a640f7cbd9521dc96171ee1dbdf909170262101a1d7a433f6f0ce2bea8d42b02
|
lib/hl7-field.sh a640f7cbd9521dc96171ee1dbdf909170262101a1d7a433f6f0ce2bea8d42b02
|
||||||
lib/hl7-schema.sh 2ba4057a214867ff4950f10057ee4ffd7149e1a82ba94b07b6857d77bf10d75f
|
lib/hl7-schema.sh 2ba4057a214867ff4950f10057ee4ffd7149e1a82ba94b07b6857d77bf10d75f
|
||||||
|
|
||||||
@ -182,6 +183,7 @@ bin/nc-regression 59a5f25fcea642433613f0933fe06dd9fa25b372e5d130e119cb466a076099
|
|||||||
bin/nc-msgs 8a938cefedbb2bad9496c6f84831788b6ac45d34e8b16fc60d8aa47c25adea76
|
bin/nc-msgs 8a938cefedbb2bad9496c6f84831788b6ac45d34e8b16fc60d8aa47c25adea76
|
||||||
bin/hl7-field cb56984dfc9342563e05cdf0e3514a64df2099d24adeaa1d5b22cc6ce298eb73
|
bin/hl7-field cb56984dfc9342563e05cdf0e3514a64df2099d24adeaa1d5b22cc6ce298eb73
|
||||||
bin/hl7-diff 2d965567ea4f737d97ea92313dfacd84b745cea74313d1be565a3234240b76f3
|
bin/hl7-diff 2d965567ea4f737d97ea92313dfacd84b745cea74313d1be565a3234240b76f3
|
||||||
|
bin/hl7-diff-set b2d6a21aa8ba55b0c6b7ae166e1dda73696e456a8c94ca0cacb686b2ae8cea30
|
||||||
bin/len2nl d73136e353d7964f097b4a50993ba2a3364550e13b7ae5a1b43af00ed8eddbe3
|
bin/len2nl d73136e353d7964f097b4a50993ba2a3364550e13b7ae5a1b43af00ed8eddbe3
|
||||||
bin/hl7-sanitize 7e2d883b4e49501eccf3db0ef27f4866dc664c3bde51fd6ad2a7804ca7de8e4e
|
bin/hl7-sanitize 7e2d883b4e49501eccf3db0ef27f4866dc664c3bde51fd6ad2a7804ca7de8e4e
|
||||||
bin/hl7-desanitize 78c5bda12e7374c056fac940ac1889ceca87f19a1e711dc658cc46b63b020925
|
bin/hl7-desanitize 78c5bda12e7374c056fac940ac1889ceca87f19a1e711dc658cc46b63b020925
|
||||||
|
|||||||
57
MANUAL.md
57
MANUAL.md
@ -402,6 +402,17 @@ lib/nc-msgs.sh ADTto_3m --limit 100 --format raw \
|
|||||||
|
|
||||||
Compare two HL7 files (or multi-message dumps) with field-level normalization.
|
Compare two HL7 files (or multi-message dumps) with field-level normalization.
|
||||||
|
|
||||||
|
**v0.9.10 (F-8, Bryan-direct live feedback):** `--format text` now labels
|
||||||
|
BOTH sides on every diff line — `SEG.FIELD left='...' right='...'` — instead
|
||||||
|
of printing only the left value with the right value rendering invisibly
|
||||||
|
blank. An empty-but-present value shows as the token `(empty)`; a component
|
||||||
|
or subcomponent that does not exist AT ALL on that side (the other side has
|
||||||
|
more components — e.g. left `MSH-9=ADT^A02^ADT_A02` vs right `MSH-9=ADT^A02`,
|
||||||
|
so `MSH.9.3` is absent, not blank, on the right) shows as `<absent>`. The
|
||||||
|
`----- message N -----` header is also now keyed off the real message index
|
||||||
|
(it used to print based on a stale running counter and could mislabel which
|
||||||
|
message a diff belonged to when an earlier message was clean).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Default — ignores MSH.7 (timestamp); shows everything else
|
# Default — ignores MSH.7 (timestamp); shows everything else
|
||||||
lib/hl7-diff.sh left.hl7 right.hl7
|
lib/hl7-diff.sh left.hl7 right.hl7
|
||||||
@ -423,6 +434,52 @@ lib/hl7-diff.sh --format tsv left.hl7 right.hl7
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## HL7 diff over a whole route SET (`hl7-diff-set` / `lib/hl7-diff-set.sh`)
|
||||||
|
|
||||||
|
One command instead of a hand-written for-loop with hardcoded route names.
|
||||||
|
Built for `hciroutetest`'s real on-disk layout: one directory holding paired
|
||||||
|
files named `<prefix>.<route>` — a baseline (e.g. Windows) prefix and a
|
||||||
|
candidate (e.g. Linux) prefix that share a set of route suffixes:
|
||||||
|
|
||||||
|
```
|
||||||
|
adt_in_regression_out_windows_single.adt_process_out
|
||||||
|
adt_in_regression_out_linux_single.adt_process_out
|
||||||
|
adt_in_regression_out_windows_single.lab_adt_out
|
||||||
|
adt_in_regression_out_linux_single.lab_adt_out
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bryan's real single-site regression directory — the actual command he runs:
|
||||||
|
hl7-diff-set adt_in_regression_out_windows_single adt_in_regression_out_linux_single \
|
||||||
|
--dir /opt/cloverleaf/cis2025.01/integrator/adt/data/regression_testing/single
|
||||||
|
|
||||||
|
# Override the ignore list / framing (both forward straight to hl7-diff)
|
||||||
|
hl7-diff-set <baseline-prefix> <candidate-prefix> --dir <path> \
|
||||||
|
--ignore "MSH.7,MSH.10" --framing len10
|
||||||
|
|
||||||
|
# Machine-parseable: one row per route, route<TAB>status<TAB>diff_count.
|
||||||
|
# The summary line prints to STDERR in this mode, so stdout stays clean tsv —
|
||||||
|
# recommended for scripting/CI gates.
|
||||||
|
hl7-diff-set <baseline-prefix> <candidate-prefix> --dir <path> --format tsv
|
||||||
|
```
|
||||||
|
|
||||||
|
Auto-discovers routes by globbing both prefixes (`<prefix>.*`), so a route
|
||||||
|
present on only ONE side is never silently dropped:
|
||||||
|
- both files present, 0 diffs → **PASS**
|
||||||
|
- both files present, N>0 diffs → **DIFF** (`--format text` also prints the
|
||||||
|
full `hl7-diff --format text` detail, indented, for that route)
|
||||||
|
- baseline route has no candidate file → **MISSING** (a dropped output IS a
|
||||||
|
regression — counts as a failure)
|
||||||
|
- candidate has a route the baseline does not → **EXTRA** (reported, also
|
||||||
|
counts as non-clean)
|
||||||
|
|
||||||
|
Prints a final summary line, e.g. `4 routes: 3 clean, 1 differs`. **Exit 0
|
||||||
|
iff every route is PASS — non-zero otherwise — so it is a single scriptable
|
||||||
|
regression gate:** `hl7-diff-set ... --dir ... || echo "regression FAILED"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Jump thread generation (`lib/nc-make-jump.sh`)
|
## Jump thread generation (`lib/nc-make-jump.sh`)
|
||||||
|
|
||||||
Generates the 3-thread cross-environment data-replay pattern: `linux_<tag>_out` on OLD, `windows_<tag>_in` + `windows_<tag>_out` in NEW's `server_jump` site. Output is plain TCL text — no file writes.
|
Generates the 3-thread cross-environment data-replay pattern: `linux_<tag>_out` on OLD, `windows_<tag>_in` + `windows_<tag>_out` in NEW's `server_jump` site. Output is plain TCL text — no file writes.
|
||||||
|
|||||||
18
bin/hl7-diff-set
Executable file
18
bin/hl7-diff-set
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# hl7-diff-set — direct, on-PATH wrapper for lib/hl7-diff-set.sh: diff a WHOLE
|
||||||
|
# SET of paired Windows-vs-Linux regression output files in one command
|
||||||
|
# (auto-discovers routes from two shared file prefixes; no hand-written
|
||||||
|
# for-loop / hardcoded route list needed). Reuses the existing hl7-diff engine
|
||||||
|
# per pair — same --ignore/--framing semantics, sane regression defaults.
|
||||||
|
#
|
||||||
|
# hl7-diff-set adt_in_regression_out_windows_single adt_in_regression_out_linux_single \
|
||||||
|
# --dir /opt/cloverleaf/cis2025.01/integrator/adt/data/regression_testing/single
|
||||||
|
#
|
||||||
|
# (no `larry tools` prefix needed). All args pass straight through; -h/--help
|
||||||
|
# is handled by the underlying tool.
|
||||||
|
set -o pipefail
|
||||||
|
_self="${BASH_SOURCE[0]}"; [ -L "$_self" ] && _self="$(readlink "$_self")"
|
||||||
|
. "$(cd "$(dirname "$_self")" && pwd)/_nc_common.sh"
|
||||||
|
lib="$(_nc_resolve_lib)" || { echo "hl7-diff-set: lib/ toolkit not found (set LARRY_LIB_DIR or LARRY_HOME)" >&2; exit 1; }
|
||||||
|
if [ $# -eq 0 ]; then exec bash "$lib/hl7-diff-set.sh" --help; fi
|
||||||
|
exec bash "$lib/hl7-diff-set.sh" "$@"
|
||||||
3
larry.sh
3
larry.sh
@ -100,7 +100,7 @@ set -o pipefail
|
|||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Config
|
# Config
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
LARRY_VERSION="0.9.9"
|
LARRY_VERSION="0.9.10"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@ -445,6 +445,7 @@ nc-msgs.sh|Native smat query: search/inspect archived messages without hcidbdump
|
|||||||
#HL7
|
#HL7
|
||||||
hl7-field.sh|Extract a field by path (PID.3, MSH.10, PV1-3-4 …) from an HL7 v2 message
|
hl7-field.sh|Extract a field by path (PID.3, MSH.10, PV1-3-4 …) from an HL7 v2 message
|
||||||
hl7-diff.sh|HL7-aware diff with field-level normalization between two messages
|
hl7-diff.sh|HL7-aware diff with field-level normalization between two messages
|
||||||
|
hl7-diff-set.sh|Run hl7-diff over a WHOLE SET of paired baseline-vs-candidate regression output files (e.g. Windows vs Linux hciroutetest dumps) in one command — auto-discovers routes from two shared file prefixes, reports PASS/DIFF/MISSING/EXTRA per route + a summary line
|
||||||
len2nl.sh|Convert length-prefixed / MLLP-framed HL7 to newline-readable form
|
len2nl.sh|Convert length-prefixed / MLLP-framed HL7 to newline-readable form
|
||||||
hl7-sanitize.sh|Tokenize PHI fields in HL7 v2 messages ([[CATEGORY_NNNN]] tokens)
|
hl7-sanitize.sh|Tokenize PHI fields in HL7 v2 messages ([[CATEGORY_NNNN]] tokens)
|
||||||
hl7-desanitize.sh|Reverse hl7-sanitize: restore original values from a token map
|
hl7-desanitize.sh|Reverse hl7-sanitize: restore original values from a token map
|
||||||
|
|||||||
206
lib/hl7-diff-set.sh
Executable file
206
lib/hl7-diff-set.sh
Executable file
@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# hl7-diff-set.sh — run hl7-diff over a WHOLE SET of paired Windows-vs-Linux
|
||||||
|
# (or any baseline-vs-candidate) regression output files in one command,
|
||||||
|
# instead of hand-writing a for-loop with hardcoded route names.
|
||||||
|
#
|
||||||
|
# Built for hciroutetest's real on-disk layout (Bryan-direct, 2026-07-23):
|
||||||
|
# one directory holding paired files named <prefix>.<route>, where TWO
|
||||||
|
# prefixes (a baseline/"windows" set and a candidate/"linux" set) share a set
|
||||||
|
# of route suffixes after the last dot, e.g.:
|
||||||
|
# adt_in_regression_out_windows_single.adt_process_out
|
||||||
|
# adt_in_regression_out_linux_single.adt_process_out
|
||||||
|
# adt_in_regression_out_windows_single.lab_adt_out
|
||||||
|
# adt_in_regression_out_linux_single.lab_adt_out
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# hl7-diff-set.sh <baseline-prefix> <candidate-prefix>
|
||||||
|
# [--dir PATH] # directory holding the paired files (default: .)
|
||||||
|
# [--ignore "FIELD,..."] # forwarded to hl7-diff. Default: MSH.7
|
||||||
|
# [--include-fields "F,.."] # forwarded to hl7-diff (overrides --ignore for that set)
|
||||||
|
# [--framing auto|nl|0x1c|len10] # forwarded to hl7-diff. Default: auto
|
||||||
|
# [--format text|tsv] # how THIS command reports. Default: text
|
||||||
|
#
|
||||||
|
# hl7-diff-set.sh adt_in_regression_out_windows_single adt_in_regression_out_linux_single \
|
||||||
|
# --dir /opt/cloverleaf/cis2025.01/integrator/adt/data/regression_testing/single
|
||||||
|
#
|
||||||
|
# What it does:
|
||||||
|
# 1. Globs "<dir>/<baseline-prefix>.*", derives each route suffix (the part
|
||||||
|
# after "<baseline-prefix>."), and pairs it with "<dir>/<candidate-prefix>.<suffix>".
|
||||||
|
# Also globs "<dir>/<candidate-prefix>.*" so a candidate-only route (no
|
||||||
|
# baseline file) is found too — nothing is silently skipped.
|
||||||
|
# 2. Runs the existing lib/hl7-diff.sh on every matched pair (this does NOT
|
||||||
|
# reimplement diffing — same engine, same --ignore/--framing semantics).
|
||||||
|
# 3. Reports PASS/DIFF/MISSING/EXTRA per route, plus a one-line summary.
|
||||||
|
#
|
||||||
|
# Per-route outcomes:
|
||||||
|
# PASS — both files present, 0 field differences (post-ignore)
|
||||||
|
# DIFF — both files present, N>0 field differences (--format text also
|
||||||
|
# shows the full hl7-diff --format text detail, indented)
|
||||||
|
# MISSING — baseline route has no matching candidate file (a dropped output
|
||||||
|
# IS a regression — this counts as a failure)
|
||||||
|
# EXTRA — candidate has a route the baseline does not (reported, also
|
||||||
|
# counts as non-clean — never silently skipped)
|
||||||
|
#
|
||||||
|
# --format text (default) prints a human report to stdout: per-route lines,
|
||||||
|
# DIFF detail indented beneath, then the summary line.
|
||||||
|
# --format tsv prints ONE machine row per route to stdout:
|
||||||
|
# route<TAB>status<TAB>diff_count
|
||||||
|
# (diff_count is "-" for MISSING/EXTRA, since no diff was actually run) —
|
||||||
|
# the summary line still prints, to STDERR, so stdout stays clean tsv.
|
||||||
|
# Recommended for scripting: hl7-diff.sh's OWN v0.9.9 text-mode message-
|
||||||
|
# header mislabel is fixed as of this release (F-8), but tsv remains the
|
||||||
|
# safer machine-parseable choice regardless.
|
||||||
|
#
|
||||||
|
# Exit codes: 0 = every route PASS (all clean); 1 = at least one route is
|
||||||
|
# DIFF/MISSING/EXTRA; 2 = usage/input error (bad flag, --dir missing, or
|
||||||
|
# neither prefix matched any file at all).
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
NC_SELF="$0"
|
||||||
|
LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
||||||
|
HL7DIFF="$LIB_DIR/hl7-diff.sh"
|
||||||
|
|
||||||
|
if [ -r "$LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
coerce_int() { local r="${1:-}" d="${2:-0}" c; c=$(printf '%s' "$r" | tr -cd '0-9'); printf '%s' "${c:-$d}"; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
die() { printf 'hl7-diff-set: %s\n' "$*" >&2; exit 2; }
|
||||||
|
|
||||||
|
DIR="."
|
||||||
|
IGNORE="MSH.7"
|
||||||
|
INCLUDE=""
|
||||||
|
FRAMING="auto"
|
||||||
|
FORMAT="text"
|
||||||
|
BASE_PREFIX=""
|
||||||
|
CAND_PREFIX=""
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--dir) shift; DIR="$1" ;;
|
||||||
|
--ignore) shift; IGNORE="$1" ;;
|
||||||
|
--include-fields) shift; INCLUDE="$1" ;;
|
||||||
|
--framing) shift; FRAMING="$1" ;;
|
||||||
|
--format) shift; FORMAT="$1" ;;
|
||||||
|
-h|--help) sed -n '2,52p' "$NC_SELF"; exit 0 ;;
|
||||||
|
-*) die "unknown flag: $1" ;;
|
||||||
|
*)
|
||||||
|
if [ -z "$BASE_PREFIX" ]; then BASE_PREFIX="$1"
|
||||||
|
elif [ -z "$CAND_PREFIX" ]; then CAND_PREFIX="$1"
|
||||||
|
else die "extra arg: $1"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -x "$HL7DIFF" ] || [ -f "$HL7DIFF" ] || die "backing tool not found: $HL7DIFF"
|
||||||
|
[ -n "$BASE_PREFIX" ] && [ -n "$CAND_PREFIX" ] || die "usage: hl7-diff-set.sh <baseline-prefix> <candidate-prefix> [--dir PATH] [--ignore FIELDS] [--framing auto|nl|0x1c|len10] [--format text|tsv] (run --help for full usage)"
|
||||||
|
[ -d "$DIR" ] || die "no such --dir: $DIR"
|
||||||
|
case "$FORMAT" in text|tsv) ;; *) die "bad --format (want text|tsv)" ;; esac
|
||||||
|
case "$FRAMING" in auto|nl|0x1c|len10) ;; *) die "bad --framing (want auto|nl|0x1c|len10)" ;; esac
|
||||||
|
|
||||||
|
# --- discovery: glob both prefixes, derive route suffixes -------------------
|
||||||
|
# Portable (bash 3.2 / MobaXterm-safe) glob: a literal no-match pattern stays
|
||||||
|
# un-expanded, so every candidate is existence-checked with `[ -e ]` before
|
||||||
|
# use — no reliance on nullglob (bash-4-leaning / shopt-dependent).
|
||||||
|
_base_suffixes=()
|
||||||
|
for _f in "$DIR/$BASE_PREFIX".*; do
|
||||||
|
[ -e "$_f" ] || continue
|
||||||
|
_bn="$(basename "$_f")"
|
||||||
|
_base_suffixes+=("${_bn#"$BASE_PREFIX".}")
|
||||||
|
done
|
||||||
|
|
||||||
|
_cand_suffixes=()
|
||||||
|
for _f in "$DIR/$CAND_PREFIX".*; do
|
||||||
|
[ -e "$_f" ] || continue
|
||||||
|
_bn="$(basename "$_f")"
|
||||||
|
_cand_suffixes+=("${_bn#"$CAND_PREFIX".}")
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#_base_suffixes[@]}" -eq 0 ] && [ "${#_cand_suffixes[@]}" -eq 0 ]; then
|
||||||
|
die "no files matched '$BASE_PREFIX.*' or '$CAND_PREFIX.*' in $DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Union of both suffix lists, sorted + de-duped, for a stable/deterministic
|
||||||
|
# route order regardless of filesystem glob order.
|
||||||
|
ROUTES="$(printf '%s\n' "${_base_suffixes[@]}" "${_cand_suffixes[@]}" | grep -v '^$' | sort -u)"
|
||||||
|
|
||||||
|
# --- run each pair ------------------------------------------------------------
|
||||||
|
diff_args=(--ignore "$IGNORE" --framing "$FRAMING")
|
||||||
|
[ -n "$INCLUDE" ] && diff_args+=(--include-fields "$INCLUDE")
|
||||||
|
|
||||||
|
n_routes=0 n_pass=0 n_diff=0 n_missing=0 n_extra=0
|
||||||
|
|
||||||
|
if [ "$FORMAT" = "text" ]; then
|
||||||
|
printf 'hl7-diff-set: baseline=%s candidate=%s dir=%s\n' "$BASE_PREFIX" "$CAND_PREFIX" "$DIR"
|
||||||
|
printf ' ignore: %s framing: %s\n\n' "$IGNORE" "$FRAMING"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r route; do
|
||||||
|
[ -z "$route" ] && continue
|
||||||
|
n_routes=$((n_routes + 1))
|
||||||
|
bfile="$DIR/$BASE_PREFIX.$route"
|
||||||
|
cfile="$DIR/$CAND_PREFIX.$route"
|
||||||
|
|
||||||
|
if [ ! -f "$bfile" ]; then
|
||||||
|
n_extra=$((n_extra + 1))
|
||||||
|
if [ "$FORMAT" = "tsv" ]; then
|
||||||
|
printf '%s\tEXTRA\t-\n' "$route"
|
||||||
|
else
|
||||||
|
printf ' %-24s EXTRA (candidate-only route — no baseline file: %s)\n' "$route" "$bfile"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ ! -f "$cfile" ]; then
|
||||||
|
n_missing=$((n_missing + 1))
|
||||||
|
if [ "$FORMAT" = "tsv" ]; then
|
||||||
|
printf '%s\tMISSING\t-\n' "$route"
|
||||||
|
else
|
||||||
|
printf ' %-24s MISSING (no candidate output — dropped route: %s)\n' "$route" "$cfile"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
count="$("$HL7DIFF" "${diff_args[@]}" --format count "$bfile" "$cfile" 2>&1)"
|
||||||
|
count="$(coerce_int "$count" 0)"
|
||||||
|
|
||||||
|
if [ "$count" -eq 0 ]; then
|
||||||
|
n_pass=$((n_pass + 1))
|
||||||
|
if [ "$FORMAT" = "tsv" ]; then
|
||||||
|
printf '%s\tPASS\t0\n' "$route"
|
||||||
|
else
|
||||||
|
printf ' %-24s PASS\n' "$route"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
n_diff=$((n_diff + 1))
|
||||||
|
if [ "$FORMAT" = "tsv" ]; then
|
||||||
|
printf '%s\tDIFF\t%s\n' "$route" "$count"
|
||||||
|
else
|
||||||
|
printf ' %-24s DIFF (%s diff(s))\n' "$route" "$count"
|
||||||
|
"$HL7DIFF" "${diff_args[@]}" --format text "$bfile" "$cfile" 2>&1 | sed 's/^/ /'
|
||||||
|
printf '\n'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<EOF_ROUTES
|
||||||
|
$ROUTES
|
||||||
|
EOF_ROUTES
|
||||||
|
|
||||||
|
# --- summary ------------------------------------------------------------------
|
||||||
|
_summary="$n_routes routes: $n_pass clean, $n_diff differs"
|
||||||
|
[ "$n_missing" -gt 0 ] && _summary="$_summary, $n_missing missing"
|
||||||
|
[ "$n_extra" -gt 0 ] && _summary="$_summary, $n_extra extra"
|
||||||
|
|
||||||
|
if [ "$FORMAT" = "tsv" ]; then
|
||||||
|
printf '%s\n' "$_summary" >&2
|
||||||
|
else
|
||||||
|
printf '%s\n' "$_summary"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$n_diff" -eq 0 ] && [ "$n_missing" -eq 0 ] && [ "$n_extra" -eq 0 ] && [ "$n_routes" -gt 0 ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
@ -32,8 +32,21 @@
|
|||||||
# --ignore MSH.7
|
# --ignore MSH.7
|
||||||
#
|
#
|
||||||
# Output (text format):
|
# Output (text format):
|
||||||
# Per-message header, then one line per differing field:
|
# Per-message header (printed once, right before that message's FIRST
|
||||||
# SEG.FIELD[.COMP[.SUB]] LEFT_VALUE RIGHT_VALUE
|
# differing line — a message with no diffs gets no header), then one line
|
||||||
|
# per differing field, BOTH sides labeled so you never have to guess which
|
||||||
|
# value came from which file:
|
||||||
|
# SEG.FIELD[.COMP[.SUB]] left='LEFT_VALUE' right='RIGHT_VALUE'
|
||||||
|
# An empty-but-present value renders as the literal token (empty); a
|
||||||
|
# component/subcomponent that does not exist AT ALL on that side (the other
|
||||||
|
# side has more components, e.g. left MSH-9=ADT^A02^ADT_A02 vs right
|
||||||
|
# MSH-9=ADT^A02 — right's 3rd component is not merely blank, it is absent)
|
||||||
|
# renders as <absent>, so "blank" and "not there" are never visually the
|
||||||
|
# same thing. (F-8, tsk-2026-07-23-hl7diff-readability, Bryan-direct
|
||||||
|
# live-feedback: previously the right-hand value printed as a genuinely
|
||||||
|
# blank/whitespace field, which looked like only ONE value was shown per
|
||||||
|
# diff line — the data was always there in the underlying lv/rv, it just
|
||||||
|
# rendered invisibly.)
|
||||||
#
|
#
|
||||||
# Exit codes: 0 identical (post-ignore), 1 differences found, 2 input error
|
# Exit codes: 0 identical (post-ignore), 1 differences found, 2 input error
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
@ -187,8 +200,14 @@ split_messages "$RIGHT" "$TMP_R"
|
|||||||
|
|
||||||
# v0.8.26: pipe the comparison output through the tty-gated sanitizer, then
|
# v0.8.26: pipe the comparison output through the tty-gated sanitizer, then
|
||||||
# propagate ${PIPESTATUS[0]} so awk's exit (0 identical, 1 differs) survives.
|
# propagate ${PIPESTATUS[0]} so awk's exit (0 identical, 1 differs) survives.
|
||||||
|
# SQ: a literal single-quote character, passed in via -v (OUTSIDE the
|
||||||
|
# single-quoted awk program text below) so the text-mode left/right quoting
|
||||||
|
# (F-8) never has to put a bare ' inside the awk source itself -- the whole
|
||||||
|
# program is one bash '...'-quoted string, and a stray ' in there would
|
||||||
|
# terminate it early and corrupt everything after (bash has no escape inside
|
||||||
|
# single quotes).
|
||||||
awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
||||||
-v LFILE="$LEFT" -v RFILE="$RIGHT" '
|
-v LFILE="$LEFT" -v RFILE="$RIGHT" -v SQ="'" '
|
||||||
# F-7 fix (2026-07-23, tsk-2026-07-23-len10, Bryan-blocking, discovered while
|
# F-7 fix (2026-07-23, tsk-2026-07-23-len10, Bryan-blocking, discovered while
|
||||||
# validating len10 self-tests -- PRE-EXISTING since v0.3.0 initial release
|
# validating len10 self-tests -- PRE-EXISTING since v0.3.0 initial release
|
||||||
# e08f030, unrelated to the len10 change): n, i, ent, arr were used in this
|
# e08f030, unrelated to the len10 change): n, i, ent, arr were used in this
|
||||||
@ -246,7 +265,15 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
function compare_field(seg, fidx, lv, rv, msg_idx, n_lc, n_rc, lc, rc, lcomp, rcomp, j, k, n_lsc, n_rsc, lsub, rsub, nm, diffs, ls, rs, ns) {
|
# F-8 fix (2026-07-23, tsk-2026-07-23-hl7diff-readability, Bryan-direct live
|
||||||
|
# feedback): lv_absent/rv_absent (new formal params, 6th/7th) tell emit()
|
||||||
|
# whether a side has NO such component/subcomponent at all (the other side
|
||||||
|
# has more components -- e.g. left MSH-9=ADT^A02^ADT_A02, right MSH-9=ADT^A02:
|
||||||
|
# component 3 on the right is ABSENT there, not merely blank) so text mode
|
||||||
|
# can render <absent> instead of a same-looking-as-blank (empty). Absence
|
||||||
|
# cascades down: if the whole field is absent, every component and
|
||||||
|
# subcomponent under it is absent too, regardless of its own split count.
|
||||||
|
function compare_field(seg, fidx, lv, rv, msg_idx, lv_absent, rv_absent, n_lc, n_rc, lc, rc, lcomp, rcomp, j, k, n_lsc, n_rsc, lsub, rsub, nm, diffs, ls, rs, ns, lc_absent, rc_absent, ls_absent, rs_absent) {
|
||||||
if (lv == rv) return 0
|
if (lv == rv) return 0
|
||||||
# Try component-level if both have ^
|
# Try component-level if both have ^
|
||||||
if (lv ~ /\^/ || rv ~ /\^/) {
|
if (lv ~ /\^/ || rv ~ /\^/) {
|
||||||
@ -257,6 +284,8 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
for (j=1; j<=nm; j++) {
|
for (j=1; j<=nm; j++) {
|
||||||
lc = (j <= n_lc) ? lcomp[j] : ""
|
lc = (j <= n_lc) ? lcomp[j] : ""
|
||||||
rc = (j <= n_rc) ? rcomp[j] : ""
|
rc = (j <= n_rc) ? rcomp[j] : ""
|
||||||
|
lc_absent = lv_absent || (j > n_lc)
|
||||||
|
rc_absent = rv_absent || (j > n_rc)
|
||||||
if (lc == rc) continue
|
if (lc == rc) continue
|
||||||
# subcomponent
|
# subcomponent
|
||||||
if (lc ~ /&/ || rc ~ /&/) {
|
if (lc ~ /&/ || rc ~ /&/) {
|
||||||
@ -266,14 +295,16 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
for (k=1; k<=ns; k++) {
|
for (k=1; k<=ns; k++) {
|
||||||
ls = (k <= n_lsc) ? lsub[k] : ""
|
ls = (k <= n_lsc) ? lsub[k] : ""
|
||||||
rs = (k <= n_rsc) ? rsub[k] : ""
|
rs = (k <= n_rsc) ? rsub[k] : ""
|
||||||
|
ls_absent = lc_absent || (k > n_lsc)
|
||||||
|
rs_absent = rc_absent || (k > n_rsc)
|
||||||
if (ls != rs && !ignored(seg, fidx, j, k)) {
|
if (ls != rs && !ignored(seg, fidx, j, k)) {
|
||||||
emit(msg_idx, seg "." fidx "." j "." k, ls, rs)
|
emit(msg_idx, seg "." fidx "." j "." k, ls, rs, ls_absent, rs_absent)
|
||||||
diffs++
|
diffs++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!ignored(seg, fidx, j, "")) {
|
if (!ignored(seg, fidx, j, "")) {
|
||||||
emit(msg_idx, seg "." fidx "." j, lc, rc)
|
emit(msg_idx, seg "." fidx "." j, lc, rc, lc_absent, rc_absent)
|
||||||
diffs++
|
diffs++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,16 +312,40 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
return diffs
|
return diffs
|
||||||
}
|
}
|
||||||
if (!ignored(seg, fidx, "", "")) {
|
if (!ignored(seg, fidx, "", "")) {
|
||||||
emit(msg_idx, seg "." fidx, lv, rv)
|
emit(msg_idx, seg "." fidx, lv, rv, lv_absent, rv_absent)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function emit(msg_idx, path, lv, rv) {
|
# F-8 fix: text mode now (a) labels both sides, left=... right=..., so the
|
||||||
|
# reader never has to guess which value belongs to which file -- the data
|
||||||
|
# was always present in lv/rv, it just rendered invisibly when the
|
||||||
|
# right-hand value happened to be empty; (b) renders an empty-but-present
|
||||||
|
# value as the literal token (empty) and a component/subcomponent that does
|
||||||
|
# not exist AT ALL on that side as <absent> (see compare_field), so blank
|
||||||
|
# and not-there are never visually indistinguishable; (c) prints the
|
||||||
|
# "----- message N -----" header lazily, right before the first emitted
|
||||||
|
# line of a message, keyed off the msg_idx actually passed in -- this
|
||||||
|
# replaces the old preemptive header print in END (which used the STALE
|
||||||
|
# global DIFF_COUNT to decide whether to print a header for the message
|
||||||
|
# about to be processed, so a clean message 1 followed by a differing
|
||||||
|
# message 2 got the message-2 diffs mislabeled under a "----- message 1
|
||||||
|
# -----" header, or no header at all). tsv/count output is untouched
|
||||||
|
# (still raw lv/rv). SQ is the single-quote character passed in via -v.
|
||||||
|
function emit(msg_idx, path, lv, rv, lv_absent, rv_absent, lv_disp, rv_disp) {
|
||||||
# count mode: accumulate only — no per-diff output (final print is in END).
|
# count mode: accumulate only — no per-diff output (final print is in END).
|
||||||
if (FMT == "tsv") printf "%d\t%s\t%s\t%s\n", msg_idx, path, lv, rv
|
if (FMT == "tsv") {
|
||||||
else if (FMT != "count") printf " %-20s %-30s %s\n", path, lv, rv
|
printf "%d\t%s\t%s\t%s\n", msg_idx, path, lv, rv
|
||||||
|
} else if (FMT == "text") {
|
||||||
|
if (msg_idx != LAST_HDR_MSG) {
|
||||||
|
printf "----- message %d -----\n", msg_idx
|
||||||
|
LAST_HDR_MSG = msg_idx
|
||||||
|
}
|
||||||
|
lv_disp = (lv_absent == 1) ? "<absent>" : ((lv == "") ? "(empty)" : lv)
|
||||||
|
rv_disp = (rv_absent == 1) ? "<absent>" : ((rv == "") ? "(empty)" : rv)
|
||||||
|
printf " %-20s left=%-24s right=%s\n", path, SQ lv_disp SQ, SQ rv_disp SQ
|
||||||
|
}
|
||||||
DIFF_COUNT++
|
DIFF_COUNT++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +362,10 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
lv = (i <= nl) ? lf[i] : ""
|
lv = (i <= nl) ? lf[i] : ""
|
||||||
rv = (i <= nr) ? rf[i] : ""
|
rv = (i <= nr) ? rf[i] : ""
|
||||||
field_num = (seg_name == "MSH") ? i : (i - 1)
|
field_num = (seg_name == "MSH") ? i : (i - 1)
|
||||||
compare_field(seg_name, field_num, lv, rv, msg_idx)
|
# F-8: field itself absent on a side (that side segment does not have
|
||||||
|
# this many pipe-delimited fields) cascades to <absent> for every
|
||||||
|
# component/subcomponent under it -- see compare_field.
|
||||||
|
compare_field(seg_name, field_num, lv, rv, msg_idx, (i > nl), (i > nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +391,11 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BEGIN { DIFF_COUNT = 0; n_l = 0; n_r = 0 }
|
# F-8: LAST_HDR_MSG tracks which message index the text-mode "----- message
|
||||||
|
# N -----" header was last printed for, so emit() can print it lazily (and
|
||||||
|
# correctly) right before the first line of that message instead of the
|
||||||
|
# old preemptive/stale-DIFF_COUNT guess in the loop below.
|
||||||
|
BEGIN { DIFF_COUNT = 0; n_l = 0; n_r = 0; LAST_HDR_MSG = 0 }
|
||||||
|
|
||||||
FNR == NR { L_MSGS[++n_l] = $0; next }
|
FNR == NR { L_MSGS[++n_l] = $0; next }
|
||||||
{ R_MSGS[++n_r] = $0 }
|
{ R_MSGS[++n_r] = $0 }
|
||||||
@ -360,7 +422,8 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
emit(i, "MESSAGE_PRESENCE", (lm != "" ? "present" : "missing"), (rm != "" ? "present" : "missing"))
|
emit(i, "MESSAGE_PRESENCE", (lm != "" ? "present" : "missing"), (rm != "" ? "present" : "missing"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (FMT == "text" && (i == 1 || DIFF_COUNT > 0)) printf "----- message %d -----\n", i
|
# F-8: header printing moved into emit() (lazy, keyed by the real
|
||||||
|
# msg_idx) -- no preemptive print here anymore. See emit()/LAST_HDR_MSG.
|
||||||
diff_message(lm, rm, i)
|
diff_message(lm, rm, i)
|
||||||
}
|
}
|
||||||
if (FMT == "count") { print DIFF_COUNT; exit (DIFF_COUNT > 0 ? 1 : 0) }
|
if (FMT == "count") { print DIFF_COUNT; exit (DIFF_COUNT > 0 ? 1 : 0) }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user