v7.1.00
Jawk 7.1 makes Jawk easy to install — curl -fsSL https://jawk.io/get | sh and you have a
jawk command — and takes a deep pass over the numeric core: printf/sprintf are now
implemented natively with POSIX AWK / gawk semantics, integral arithmetic is computed exactly in
64 bits, and getline follows gawk down to which variables each form sets. Along the way, scripts
run measurably faster.
📦 One-command installation
- Install the
jawkexecutable with one command —curl -fsSL https://jawk.io/get | shon
Linux/macOS,irm https://jawk.io/get.ps1 | iexon Windows. Nosudo: the launcher goes into
~/.local/bin(or%LOCALAPPDATA%\Jawk), and it locates a Java runtime (Java 8 or later) at
run time, so upgrading Java never requires a reinstall.JAWK_VERSIONpins a release,
JAWK_INSTALL_DIRchanges where the launcher lands. Every release now also publishes a
version-lessjawk-standalone.jaralias with.sha256checksums, so
releases/latest/download/jawk-standalone.jaris a stable download URL
by @bertysentry in #584 JAWK_CLASSPATHin the launchers — set it and the launcher runs the CLI with the standalone
jar plus your entries on the JVM class path, so custom
extension jars load with-l <fully.qualified.ClassName>.
Previously the launcher always usedjava -jar, which ignores every class path setting
by @bertysentry in #588- CI smoke tests for both installers — the real scripts are exercised end to end on every
change, so a brokenjawk.io/getcannot reach the site
by @bertysentry in #587
🖨️ Native printf with AWK semantics
printf/sprintf no longer delegate to the Printf4J library, which emulated glibc's printf();
they are implemented in the new io.jawk.jrt.AwkPrintf with the semantics POSIX specifies and gawk
implements, verified against gawk 5.0/5.1
by @bertysentry in #543
%suses AWK's number-to-string rules — integral values print without a fractional part
(printf "%s", iafteri++prints1, not1.0), non-integral values honor the script's
currentCONVFMT.%cprints the character for a numeric code point — numbers, numeric strings, and fields
alike — or the first character of a string value.- Dynamic precision
%.*fjoins dynamic width%*d, negative values included; gawk
positional specifiers (%2$s) and the'grouping flag (%'d) are supported. - Out-of-range integers follow gawk — negative values wrap to unsigned 64-bit for
%u/%o/%x/%X, and values beyond 64 bits print their full decimal expansion for%d/%i. - NaN and infinities print as
nan,inf,-infinstead of Java'sNaN/Infinity— in
print,printf, and number-to-string conversions. - Correct rounding —
%e/%f/%ground the exact binary value of the double, halfway cases
to even, like the C library gawk uses (printf "%.0f", 2.5prints2), and the same rounding
applies throughCONVFMTandOFMT(#548). - Too few arguments is a fatal error, as in gawk; unknown conversions — including
%n, which
Printf4J turned into a newline — print verbatim without consuming an argument. - New
JavaStringFormatAwkSinkfor Java embedders: a sink whoseprintf/sprintfuse
String.format(...), giving scripts Java-only conversions such as%,dand%tY.
🔢 Exact numbers
- Integral arithmetic is computed exactly in 64 bits —
+,-,*, an evenly-dividing/,
%, unary±,++/--and the compound assignments, on variables, array elements, and fields
alike, keep all their digits beyond 2^53:print 9007199254740992 + 1prints9007199254740993,
where gawk computes in doubles and prints9007199254740992unless run with-M. Overflow falls
back to floating point, and comparisons between two exact integers are exact as well. Values
beyond the 64-bit range are no longer saturated either:print 2^100prints the full
1267650600228229401496703205376
by @bertysentry in #552 - Fields keep their numeric value through
+=and++, like=always did, and rebuilding
$0converts numeric fields withCONVFMT, as POSIX requires and gawk does:
{ $1 = 0.1 + 0.2; print }prints0.3 ...instead of the raw0.30000000000000004 ...
by @bertysentry in #552 - Exponent literals are lexed as one number —
print 2e3prints2000; previously the lexer
stopped before the exponent, so2e3parsed as2concatenated with an uninitialized variable
e3. Long numeric strings now keep all their digits instead of stopping after 26 characters, an
integer literal too large for 64 bits is a floating-point constant instead of a
NumberFormatException, andsubstr()'s length argument accepts every AWK number form
by @bertysentry in #544 - Array subscripts convert with the
CONVFMTin effect when the subscript is used, as POSIX
requires and gawk does, so a laterCONVFMTchange no longer retroactively rewrites existing
keys — and integral subscripts beyond the 64-bit range key as their exact digits
by @bertysentry in #551 - Blank-padded numeric strings are recognized as strnums — the record
" 12 "compares
numerically ($0 == 12is true), as in gawk; string constants such asx = " 12 "still compare
as strings (#550)
by @bertysentry in #573
📥 getline, redirections, and special filenames
- Redirected
getlinesets exactly the variables gawk documents —getline < fileand
cmd | getlineset$0andNFonly;getline var < fileandcmd | getline varsetvar
only. Previously every redirected form also advancedNR, so a side read shifted the record
numbers of the whole main input, and took overFILENAME
by @bertysentry in #572 - A
getlinethat cannot open its file returns-1withERRNOcarrying the gawk-style
description, so the idiomaticwhile ((getline line < f) > 0)guard and probing for optional
files work; previously the underlying JavaIOExceptionaborted the whole script
by @bertysentry in #572 - A
getlinethat reads nothing leaves its target untouched, as POSIX requires — no more
clearing$0/NFor assigning the empty string at end of input or on error
by @bertysentry in #572 getlineinto a field —getline $n < file,cmd | getline $n, and plaingetline $n—
stores the record into that field and rebuilds$0; previously any of these forms corrupted the
interpreter's operand stack and crashed the script on the first record
by @bertysentry in #572- The gawk special filenames
/dev/stdout,/dev/stderr,/dev/stdinand their/dev/fd/N
spellings route to the streams the process already holds open, instead of being opened — and
truncated — as regular files.close()flushes the redirection and reports success without ever
closing the underlying stream
by @bertysentry in #564 /dev/nulldesignates the null device on Windows (NUL) in redirections, ingetline, and
as an input operand, as gawk's Windows port does. Previously it was opened as a relative path,
creating and truncatingdev\null— and keeping the output that was meant to be discarded
by @bertysentry in #568
🖥️ CLI and process integration
- POSIX attached option-arguments —
-fprog.awk,-vx=1,-F:and the Jawk-specific-L,
-K,-lnow work like their spaced forms, as in gawk, mawk, BWK awk, and goawk; previously the
glued form was rejected withUnknown parameter
by @bertysentry in #576 -V/--versionprints the Jawk version and the Java runtime that executes it, and the
usage printed by-hnow names the command you actually typed — the installed launchers pass it
through the newJAWK_PROGRAM_NAMEvariable — instead of always describing ajava -jar
invocation the user never made
by @bertysentry in #593system()and command-pipe children inherit the standard input, as POSIX requires and gawk,
mawk, and BWK awk do:echo hi | jawk 'BEGIN { "sort" | getline l; print l }'printshi, and
terminal-aware commands such as"stty size" | getlinecan reach the controlling terminal.
Previously the child's standard input was always closed, so stdin filters read nothing
by @bertysentry in #577
🧩 Language and correctness
nextis accepted inside user-defined functions, as in gawk, mawk, and BWK awk: called from
an input rule it abandons the current record and unwinds the active calls; called fromBEGIN,
END,BEGINFILE, orENDFILEit is a runtime fatal error, matching gawk. Previously anynext
inside a function was rejected at compile time
by @bertysentry in #582- Conditional expressions feeding a further operation now optimize correctly — shapes like
(v ? v : 24) * 2,-(v ? 5 : 24), or$(v ? 1 : 2). The peephole literal fold used to merge
the false branch's literal with the operation at the branches' join point, so(60 ? 60 : 24) * 2
yielded 48 and leaked a value onto the operand stack that displaced laterprintoperands
by @bertysentry in #579 --profileno longer leaks function timings onnextfile— a call abandoned bynextfileis
recorded as exited with its actual duration, instead of leaking a stack entry (unbounded memory
growth on long profiled runs) and being reported with wildly wrong durations at program exit
by @bertysentry in #581
⚡ Performance
- Flat
Object[]operand stack replaces the boxedArrayDequein the AVM:push/popare
single array accesses the JIT can fully inline, and argument slices are bulk-copied. −13.5% on a
tight arithmetic loop, −5% to −14% across the expression benchmarks
by @bertysentry in #555 AwkPrintfhot paths — per-locale symbol cache, parsed-format cache, and a guarded rounding
fast path: 1.3×–9.8× faster formatting (%fof0.13.0×,CONVFMTof3.141593.2×), with
output verified bit-identical — 19.6M rounding comparisons against the exact-expansion reference
and 600k differential-fuzz cases, zero mismatches
by @bertysentry in #560- Non-throwing associative-array key normalization — every non-integer key (all SUBSEP-joined
arr[x,y]keys, all string keys) used to allocate aNumberFormatExceptionand fill in its stack
trace, ~600 ns on everyget,put, andintest; it is now a hand-rolled scan with parity to
Long.parseLonglocked in by tests
by @bertysentry in #561 AVM.executeTupleskept under HotSpot's huge-method limit — the dispatch loop sat at 7994
bytecodes (8013 under ECJ), right at the 8000-bytecode cliff past which HotSpot never JIT-compiles
a method at all: crossing it made every AWK script ~4× slower, silently. It is now 6728 bytecodes,
with a test that fails the build above 7500
by @bertysentry in #563
⚠️ Breaking changes
AwkSink.printf(...)andAwkSink.sprintf(...)now takeCONVFMT—printfreceives it
betweenofmtandformat, andsprintf(convfmt, format, values...)is the single sprintf
method and the customization point. Custom sinks must be updated. Theorg.metricshub:printf4j
dependency is gone (#543).- Programs precompiled with an earlier version are rejected and must be recompiled: they may
carry constants folded in floating point (#552). - The broken
stdinextension is removed, along with theBlockObject/BlockManagerAPI whose
only user it was. ItsStdinHasInput()andStdinBlock()never reported a pending end of input,
hanging every read loop that guarded itsStdinGetline()calls, and no other AWK implementation
provides these functions.-l stdinis now rejected withUnknown extension 'stdin'; read
standard input through the regular input loop,getline, or/dev/stdin
by @bertysentry in #594
Every user-visible change above is described with its before/after on the
behavior changes page.
📚 Documentation
- Pre-release documentation audit — the site and README now lead with the
jawkcommand, the
installation guide is simplified, and over-broad statements were tightened
by @bertysentry in #589 - Release plumbing for v7.1.00-SNAPSHOT by @github-actions[bot] in #542
Full Changelog: v7.0.01...v7.1.00
Dependabot
- build(deps): bump org.apache.maven:apache-maven from 3.8.6 to 3.9.16 by @dependabot[bot] in #554