just-bash
3.6.0 · 1 Oct 2026Apache-2.01.2M / weekRepositoryHomepagenpmAtom feed
Latest releases
All 100 releases- 3.x · latest line
3.6.0
1 Oct 2026Add the
mktempcommand. It creates a unique temporary file (mode 0600) or directory (-d, mode 0700) and prints its path, supporting-p/--tmpdir[=DIR],-t,-u/--dry-run,-q/--quiet,--suffix=SUFF, GNU-styleTEMPLATEexpansion,--helpand--version. The default directory is$TMPDIRwhen set and non-empty, otherwise/tmp, so sandboxed embedders that pointTMPDIRat a writable directory get a usable path. Random name characters come from the platform CSPRNG (crypto.getRandomValues), and existing paths are never returned.Adds an optional
createExclusive(path, { mode, directory })method toIFileSystem, which creates an entry only if the name is free and applies the mode at creation time rather than via a follow-upchmod. All built-in filesystems implement it. The member is optional, so externalIFileSystemimplementations remain source compatible;mktempreports that such a filesystem cannot create atomically rather than substituting a non-atomic sequence. The accompanyingCreateExclusiveOptionstype is exported alongside the existingMkdirOptions/RmOptions.Add the
yescommand.yes [STRING]...repeats a line built from its operands (ywhen there are none), soyes | head -3andyes | some-promptwork instead of failing with exit 127. Because pipeline stages here run to completion rather than streaming, the stream is finite: it ends afterexecutionLimits.maxLoopIterationslines, or earlier if the repeated line would exceed the output size limit.interpreter: preserve complete associative-array values in
declare -Acompound assignmentsQuoted values containing whitespace were reconstructed without quoting before the
declarebuiltin parsed them, so every value was silently truncated at its first space. Associative-array declarations now retain whitespace and other quoted content.
Minor Changes
Add the
mktempcommand. It creates a unique temporary file (mode 0600) or directory (-d, mode 0700) and prints its path, supporting-p/--tmpdir[=DIR],-t,-u/--dry-run,-q/--quiet,--suffix=SUFF, GNU-styleTEMPLATEexpansion,--helpand--version. The default directory is$TMPDIRwhen set and non-empty, otherwise/tmp, so sandboxed embedders that pointTMPDIRat a writable directory get a usable path. Random name characters come from the platform CSPRNG (crypto.getRandomValues), and existing paths are never returned.Adds an optional
createExclusive(path, { mode, directory })method toIFileSystem, which creates an entry only if the name is free and applies the mode at creation time rather than via a follow-upchmod. All built-in filesystems implement it. The member is optional, so externalIFileSystemimplementations remain source compatible;mktempreports that such a filesystem cannot create atomically rather than substituting a non-atomic sequence. The accompanyingCreateExclusiveOptionstype is exported alongside the existingMkdirOptions/RmOptions.Add the
yescommand.yes [STRING]...repeats a line built from its operands (ywhen there are none), soyes | head -3andyes | some-promptwork instead of failing with exit 127. Because pipeline stages here run to completion rather than streaming, the stream is finite: it ends afterexecutionLimits.maxLoopIterationslines, or earlier if the repeated line would exceed the output size limit.
Patch Changes
interpreter: preserve complete associative-array values in
declare -Acompound assignmentsQuoted values containing whitespace were reconstructed without quoting before the
declarebuiltin parsed them, so every value was silently truncated at its first space. Associative-array declarations now retain whitespace and other quoted content.fs: keep the Buffer-less
fromBufferfallback under the argument limitWithout
Buffer(for example in a Chrome extension service worker),fromBufferconvertedbase64,binaryandlatin1content by spreading 64KB chunks intoString.fromCharCode, which can exceed the engine's argument limit and throwRangeError: Maximum call stack size exceededon a 64KBcat. The fallback now converts in 8KB chunks.Handle accessor descriptors when installing module defense-in-depth proxies on Bun, preserving blocking behavior and original descriptors on teardown.
find: report a directory it cannot read and keep going
A
readdirfailure inside the traversal threw out of the whole search, so one unreadable directory endedfindwith no results: exit 1 andfind: EACCES: permission denied, scandir '<path>'on its own, and exit 0 with nothing at all once piped throughheadwith stderr silenced. Every macOS home directory holds such a directory (.Trash, several underLibrary), so a recursivefindover a mounted home directory never returned anything.GNU find names the directory on stderr, continues with everything else, and exits 1 at the end. It now does the same here. The message is
find: <path>: Permission denied, with the phrase taken from the errno alone, so nothing from the underlying error's text reaches the output. A failure that is not one of the errnos a directory read can produce (a cancellation, an execution limit, a filesystem policy refusal) still ends the search as before.Messages are emitted in traversal order beside the node's own output, whatever order the parallel batch settled in, and a failed read still counts toward the trace's
readdirCallsandreaddirTime.Match real jq behavior for
to_entrieson arrays (numeric-key entries instead of null) andtonumberon empty or whitespace-only strings (error instead of 0).interpreter: give a loop left via
break/continuestatus 0 instead of the last command'sbreakandcontinueare builtins that return 0, and they are the last command a loop body runs. A loop exited through them reported the status of whatever ran before thebreakinstead:while :; do false; break; done; echo $? # was 1, bash says 0 for i in 1; do false; break; done; echo $? # was 1, bash says 0 for i in 1 2; do false; continue; done; echo $? # was 1, bash says 0
All four loop forms were affected —
for, C-stylefor,while,until— as was abreak/continuein awhilecondition and a multi-levelbreak 2unwinding through an enclosing loop.The stale status is invisible until something reads
$?, and underset -ethe phantom failing loop ends the script with no output and no diagnostic:set -euo pipefail while :; do [ 5 -eq 0 ] && break # correctly exempt from errexit as the left operand of && break done echo ok # never ran
$?moves with the status, so the next iteration sees it too — aforloop runs nothing between thecontinueand the next iteration's first command, and would otherwise still expose the failure there:for i in 1 2; do echo "$i:$?"; false; continue; done # was 1:0 2:1, bash says 1:0 2:0
continuesets the status at the point it runs but does not pin it: a later iteration still overwrites it, sofor i in 1 2; do if [ $i = 1 ]; then true; continue; fi; false; doneis still 1. A loop that ends normally is unchanged and reports its last command, andreturnfrom a function still reports the last command rather than 0.Reported downstream as ai-ecoverse/slicc#2978.
Give
MountableFssynchronousmkdirSyncandwriteFileSync, routed to the filesystem that owns the path, soBashsets up/bin,/dev,/proc,/tmpand the working directory in its base. Paths on a filesystem without synchronous writes throwENOSYSand are left out of the layout.Fix default, assignment, and alternative parameter expansions used as an entire double-quoted word under
set -u. For example,echo "${U:-fallback}"now printsfallbackwhenUis unset instead of reporting "unbound variable". This also lets scripts safely check optional CI variables such asGITHUB_STEP_SUMMARY.Bundle the patched
smol-toml1.9.0.The Node bundle inlines
smol-toml, so a consumer can't pick up its fix through its own lockfile. Only a just-bash release carries it.Use a virtual executable name for CPython instead of the host worker path, avoiding script finalization failures under long installation paths without weakening worker protections.
Repeated
grep -eoptions overwrote the earlier patterns, so only the last one was used. All-epatterns now combine.Host operations that settle after a cancelled execution no longer surface as a process-level
unhandledRejection.Aborting or timing out an execution while the worker bridge was awaiting a host tool (or an
exec) left that operation to settle after the defense-in-depth box deactivated. The box'sPromise.prototype.thenguard, which blocks sandbox callbacks once their execution ends, then dropped the handlers meant to consume that result: a late rejection crashed processes that treat unhandled rejections as fatal, and a late success never reached the bridge. Host-side settlement now goes throughawait, which bypasses the guardedthen.Keep leading blanks after
a\,i\andc\insedtext, as GNU does, including blanks escaped with a backslash. The one-linea textform now strips every leading blank instead of only the first.Fix intermittent
python3failures withOSError: [Errno 29] I/O erroron a busy host. A wakeup left over from the previous bridge call could end the wait for the next call before the main thread answered it. The worker now keeps waiting until the call is answered, within the same operation timeout.timeout: a deadline that lands while a command is still loading no longer aborts the rest of the script
timeouton a cold command — the first use of any lazily loaded command — could return 124 and still kill every statement after it. Cancelled invocations now stop waiting for the load and report cancellation instead.
3.4.2
22 Aug 2026Include the security and transform declaration trees referenced by the package entrypoints and worker declarations in published packages.
3.4.1
19 Aug 2026Prevent defense-in-depth violation reporting from recursively overflowing the call stack in host runtimes that wrap
Date.now(), honor configured main-thread violation exclusions, include actionable exclusion guidance for configurable violations, and keep constructor-execution protections non-excludable.
3.4.0
18 Aug 2026Provide the shadowed bundled command to custom command overrides through
CommandContext.origCommand.
3.3.0
13 Aug 2026jq: add external-argument flags (
--arg,--argjson,--rawfile,--slurpfile,--args,--jsonargs) and the$ARGSobject ($ARGS.named/$ARGS.positional), matching real jq 1.7.1 behavior including exit codes, error messages, and prototype-sensitive key handling.Support user file descriptors (fd >= 3).
exec 3< file,N< file/N> file/N>> fileon any command,read -u N,read <&N,>&N,N<&M, andN<&-now go through a real descriptor table: a descriptor carries one shared read position,execkeeps it open until it is closed, and every other construct — includingdone N< fileon a loop — gets it only for the duration of that command.Treat a
-FILE operand ingrepas standard input, matching GNU.grep PATTERN -now reads stdin instead of failing with "No such file or directory", stdin is labelled(standard input)in the multi-file prefix and in-l/-L/-coutput, repeated-operands see the stream drained by the first one, and-is exempt from-rrecursion and--include/--excludefiltering.
Minor Changes
jq: add external-argument flags (
--arg,--argjson,--rawfile,--slurpfile,--args,--jsonargs) and the$ARGSobject ($ARGS.named/$ARGS.positional), matching real jq 1.7.1 behavior including exit codes, error messages, and prototype-sensitive key handling.Support user file descriptors (fd >= 3).
exec 3< file,N< file/N> file/N>> fileon any command,read -u N,read <&N,>&N,N<&M, andN<&-now go through a real descriptor table: a descriptor carries one shared read position,execkeeps it open until it is closed, and every other construct — includingdone N< fileon a loop — gets it only for the duration of that command.Treat a
-FILE operand ingrepas standard input, matching GNU.grep PATTERN -now reads stdin instead of failing with "No such file or directory", stdin is labelled(standard input)in the multi-file prefix and in-l/-L/-coutput, repeated-operands see the stream drained by the first one, and-is exempt from-rrecursion and--include/--excludefiltering.Support process substitution
<(cmd)and>(cmd).<(cmd)runscmdand substitutes a readable/dev/fd/Npath backed by an
in-memory file;>(cmd)substitutes a writable path whose contents are fed tocmdonce the outer command finishes. Descriptors are numbered from 63
downwards like bash and released when the command that opened them completes.
Process substitutions retain their surrounding word context in assignments,
conditionals, regular expressions, and heredoc delimiters. Previously any use
raisedParse error: Expected redirection target.Add
grep -f FILE/--file=FILEto read patterns from a file (one per line). Patterns from-fOR-combine with-epatterns and with each other,-f -reads patterns from stdin, empty pattern lines match every line, and an empty pattern file selects nothing (exit 1). Newline-separatedPATTERNSoperands are now split into individual patterns, and-xgroups alternatives correctly (^(?:a|b)$).
Patch Changes
Parse and serialize bare file descriptor variable redirections such as
{output}>output.logand{input}<<EOF. Bare redirects create their target with a command-scoped descriptor, while named command forms keep the allocated descriptor available.Fix escaped reserved words being parsed as shell syntax. Unquoted escapes now retain their provenance through lexing and word parsing, including when the word is serialized back to Bash.
Fix
grep -Lexit status to match GNU grep. The status reports whether a line
was selected, not whether a filename was printed, sogrep -Lnow exits 0 when
every file matched (printing nothing) and 1 when no file matched (printing every
name) — previously these were inverted.network: restore private-range-enforced requests from the bundled build
Every request made with
denyPrivateRangesenabled failed withNetwork access denied: DNS pinning unavailable for private IP enforcement, socurlcould not reach any host at all. The published ESM bundle was affected; source consumers and the CommonJS bundle were not.The pinned connection owner reads
Agentandfetchoff a dynamicimport("undici"). Node's resolution of the package exposes those as named exports, but the ESM build inlines undici's CommonJS module into a chunk whose namespace carries it underdefaultalone, soAgentwasundefinedand theTypeErrorfrom constructing it was reported as a runtime incapable of pinning.The namespace is now normalized before the transport is read off it, which also covers a consumer that bundles just-bash further.
Stop command groups, function bodies and
evalfrom rewinding stdin they never replaced, so{ { read a; }; read b; }givesbthe second line instead of replaying the first.Reject unsupported command-leading reserved words in every parser context instead of discarding them or executing them as simple commands. Unknown command AST nodes now fail explicitly instead of returning a successful result.
Restore command groups and subshells after the process-substitution and stdin-ownership changes were combined without forwarding ownership through inner command dispatch.
Apply output redirections attached to
whileanduntilloops.while true; do echo x; break; done >/dev/nullno longer leaks its output to the caller, and> file,>>,2>,2>&1,&>and>|now behave on loops the way they already did onforandcase.untilloops also gained the input-redirection handlingwhileloops already had, sountil ! read l; do ...; done < filereads from the file. A loop now only restores stdin it owns, so reading inside a loop no longer rewinds an enclosing group's read position:printf 'a\nb\n' | { while read x; do break; done; read y; }seesy=b.Stop pipelines from draining the enclosing shell's stdin, so
while read …; do … | …; done < fileruns once per line again.Process redirections through policy-driven transactions that preserve Bash ordering, descriptor lifetimes, persistent
execroutes, compound-command stdin, control-flow output routing, and shared read-write descriptor positions without duplicate expansion or opening.FS sym-link hardening
sqlite3: report failed statements on stderr and exit non-zero without
-bailA statement that failed was written to stdout as
Error: ...and the command still exited0unless-bailwas passed. Realsqlite3writes the error to stderr and exits1in either mode;-bailonly decides whether the remaining statements still run.Two consequences for callers:
sqlite3 db "SELECT ..." > out.csvsilently wrote the error text into the data file, andsqlite3 db "..." && next-steprannext-stepafter the query had failed, so a shell script could not detect a bad query without opting into-bailand losing the ability to see later statements.Errors now accumulate on stderr in statement order and the exit status is
1whenever any statement failed. Successful runs are unchanged,-bailstill stops at the first failure, and the partial stdout produced before a failure is still emitted. Writeback of a partially-successful script is also unchanged.Preserve whether a here-document ended at its delimiter or at end-of-input. Unterminated final body lines now receive Bash's trailing newline, backslash-newline continuations are removed during expansion, and serialization rejects unterminated documents rather than manufacturing a closing delimiter.
Dependency tracking · free for 3 projects
Know when your own dependencies move
just-bash shipped 3 new majors in the last 12 months, among 100 releases. Inlinr shows which of your projects are behind, and what the upgrade breaks.
Advisories reach you
An email when a critical or high advisory affects a project, before the next install.
Upgrades checked first
Peer ranges, Node, ESM-only and types changes read before you bump.
Only what matters
Majors and breaking notes stay; patches and sibling bumps fold into one line.
Registry facts last checked 22 Oct 2026. Releases and advisories last read 22 Oct 2026.